首页 > 编程语言 >C#的重载决策

C#的重载决策

时间:2023-07-31 22:14:54浏览次数:34  
标签:函数 C# 决策 int M1 M2 重载 参数

重载是许多编程语言支持的特性。所谓重载,就是指可以定义多个名称相同但参数(个数、类型和顺序)不同的方法(函数)。先来看一个例子:

void Main()
{
    char cvalue = 'a';
    male m = new male();
    m.write(cvalue);
}

class human
{
    public void write(char value)
    {
        Console.WriteLine("char:" + value);
    }
}

class male : human
{
    public void write(int value)
    {
        Console.WriteLine("int:" + value);
    }
}

这个例子中,父类human中有个一个参数类型为char的函数write,子类(derived class)male中提供了参数类型为int的重载函数,在Main方法中实例化了一个子类male的对象m,对象m调用write方法,并传递char类型的参数。最终执行的结果是int:97,而不是char:a。为何在函数调用的时候,没有找到父类中参数类型完全匹配的函数,而是进行了类型转换呢?
有人说这是因为 .NET的类型推断(type inference)不够智能。其实,这里并不涉及类型推断,因为类型推断主要是针对隐式类型和泛型的。

这里是由重载决策机制决定的。重载决策是一种绑定时机制,用于在给定参数列表和一组候选函数成员的情况下选择要调用的最佳函数成员。c#中支持重载的有以下几种情况:

  • 方法(函数)重载
  • 构造函数重载
  • 索引器重载
  • 操作符重载

虽然上述四种情况都有自己独有地定义重载函数和参数列表的方式,但是重载决策选择最佳函数调用地过程是一致的。

  • 首先,根据给定的参数列表从候选函数集合中找到适用的函数成员,如果没有找到则会报编译错误
  • 然后,从适用的候选函数成员集中找到最佳函数成员。如果集合只包含一个函数成员,则该函数成员是最佳函数成员。否则,根据更好的函数成员规则,找到相对于其他函数成员更好的一个函数成员作为最佳函数,如果没有一个函数成员优于所有其他函数成员,则函数成员调用不明确,并且会发生绑定时错误。

根据C# Language Specification成员查找(Member lookup)的描述,方法调用的候选集不包括标记为override的方法。根据方法调用(Method invocations)的描述,子类中只要有一个方法适用,则父类的函数不是候选函数。这也就解释了开篇的例子中为何没有选择父类中参数类型完全匹配的函数。

适用的函数成员

当满足以下所有条件时,函数成员被称为与参数列表A的适用函数成员:

  • 参数列表A中的每实参都对应于函数成员声明中的一个参数,每个形参最多对应一个实参,并且任何没有实参对应的形参都是可选形参。
  • 参数列表A中的每个实参,实参的传递模式与对应形参的传递模式相同。
    • 对于值类型参数或者参数数组,允许实参到对应形参存在隐式转换
    • 对于带有ref或者out修饰符的参数,允许实参到对应形参存在恒等转换(identity conversion)
    • 对于带有in修饰符的参数,允许实参到对应形参存在恒等转换(identity conversion)
    • 对于in传递模式,如果没有带in修饰符,允许实参到对应形参存在隐式转换

接下来用几个例子来说明函数是否适用

void Main()
{
    int i = 10; uint ui = 34U;var vi=5;dynamic di=5;

    M1(in i);   // M1(in int)合适
    M1(in ui);  // 没有精准匹配, 因此M1(in int)不适用
    M1(i);      // M1(int) 和 M1(in int)都适用
    M1(ui);     // uint到int不存在隐式转换,所以M1(int) 不适用
    M1(in vi);   //恒等转换(identity conversion),M1(in int)适用

    M2(ui);     //uint隐式转换为long,因此M2(long)适用;同时传参没带in修饰符,允许装箱为object的隐式转换,M2(in object)适用
    M2(di);     //不适用
    M2(in di)   //恒等转换(identity conversion),M2(in object)适用
    M2(in vi)   //恒等转换(identity conversion),M2(in object)适用
    
    M3(ui)     //ref和out传递模式不支持忽略ref和out修饰符匹配,因此M3(ref object)不适用
}

public static void M1(int p1) { Console.WriteLine("M1 int:"+p1); }
public static void M1(in int p1) { Console.WriteLine("M1 in int:"+p1); }


public static void M2(long p1) { Console.WriteLine("M2 long:"+p1); }
public static void M2(in object p1) { Console.WriteLine("M2 in object"+p1); }

public static void M3(ref object p1) { Console.WriteLine("M3 ref object" + p1); }

更好的成员函数

假设调用函数时传递的参数为{E₁, E₂, ..., Eᵥ},有两个适用的函数Mᵥ(P₁, P₂, ..., Pᵥ)Mₓ(Q₁, Q₂, ..., Qᵥ),满足以下条件时则认为Mᵥ是更合适的函数:

  • 对于每一个参数,从EᵥQᵥ的隐式转换没有比EᵥPᵥ的隐式转换更好
  • 至少有一个参数满足,从EᵥPᵥ的转换比EᵥQᵥ的转换好。

如果按照上述规则比较,函数Mᵥ(P₁, P₂, ..., Pᵥ)Mₓ(Q₁, Q₂, ..., Qᵥ)是等价的(例如每个PᵢQᵢ是恒等转换关系),则继续根据以下规则判断更好的函数:

  • 如果Mᵢ是非泛型方法,而Mₑ是泛型方法,则认为Mᵢ更合适
  • 如果Mᵢ是普通方法,而Mᵢ是扩展方法,则认为Mᵢ更合适
  • 如果MᵢMᵢ都是扩展方法,并且Mᵢ的参数更少,则认为Mᵢ更合适
  • 如果Mᵢ的参数中有比Mᵢ的对应参数更具体地类型,则认为Mᵢ更合适

更好的参数传递模式

当两个重载方法中对应的形参仅在形参传递模式上不同,并且两个函数形参中的一个具有值传递模式,例如

public static void M1(int p1) { ... }
public static void M1(in int p1) { ... }

前边适用的函数成员部分提到,调用M(10)方法时,两个重载方法都适用。这种情况下,值传递模式是更好的参数传递模式。

ref和out传递模式不支持忽略ref和out修饰符匹配,必须精准匹配

标签:函数,C#,决策,int,M1,M2,重载,参数
From: https://www.cnblogs.com/czwy/p/17594654.html

相关文章

  • C#中Socket编程,异步实现Server端定时发送消息
    在最近项目需求中,要求服务端定时向客服端发送消息。由于客户端从机的特性,只能接收Server发送的消息后回复,不能主动向服务端发送消息。起初,并未使用异步的方法进行编程,使用了Accept()、Revice()等方法。由于从机不能主动发送消息的特性,并未考虑到从机断电不能接收到Server消息的情......
  • JavaScript
    1、什么是JavaScriptJavaScript是一门世界上最流行的脚本语言一个合格的后端人员,必须精通JavaScript历史:https://blog.csdn.net/kese7952/article/details/793578682、快速入门2.1、引入JavaScript1、内部标签<script>//......</script>2、外部标签abs.js//......
  • HBase-compact的作用、两种实现方式及区别
    在hbase中每当有memstore数据flush到磁盘之后,就形成一个storefile,当storeFile的数量达到一定程度后,就需要将storefile文件来进行compaction操作。Compact的作用:①合并文件②清除过期,多余版本的数据③提高读写数据的效率HBase中实现了两种compaction的方式:minorandmaj......
  • *CTF和nssctf#16的wp
    *ctf2023fcalc分析程序本题存在漏洞,是生活中很容易犯的错误,就是循环或者判断的时候没有注意多一还是少一,这种会发生很严重的问题。比如这个题在过滤数字的时候没有过滤掉0,所以输入0的时候会跳转到栈的内容,从而被攻击者执行shellcode。不过本题目不能直接执行,因为存在一个对浮......
  • 字符编码笔记:ASCII,Unicode和UT…
    字符编码笔记:ASCII,Unicode和UTF-8作者:阮一峰今天中午,我突然想搞清楚Unicode和UTF-8之间的关系,于是就开始在网上查资料。结果,这个问题比我想象的复杂,从午饭后一直看到晚上9点,才算初步搞清楚。下面就是我的笔记,主要用来整理自己的思路。但是,我尽量试图写得通俗易懂,希望能对......
  • PCB layout,或称为PCB设计,是将电子元件和连接线路等布局到电路板上的过程。
    1.设计思路在进行PCBlayout前,需要明确电路板的设计目标、功能、性能指标等,按照最终需求来设计电路板的参数、尺寸和针脚安排等。2.元器件部署按照较理想的排列方式,分配和放置电子元件的位置和布局,也就是确定元器件的摆放位置、取向和连线顺序,配合特定的机械框架基本......
  • leetcode-n-sum总结
    总结一下leetcode中遇见的2-sum,3-sum,4-sum问题,并扩展到n-sum。1.两数之和-力扣(LeetCode)梦开始的地方,不多说。classSolution{publicint[]twoSum(int[]nums,inttarget){Map<Integer,Integer>map=newHashMap<>();for(inti=0;i<......
  • pytorch-tensor属性统计(norm,max,min...)
    statistics▪norm(范数)▪mean,sum(平均值,求和)▪prod(累乘)▪max,min,argmin,argmax▪kthvalue,topk(第k大)norm(范式)这里面有一范式和二范式。一范式:\[||x||_1=\sum_k|x_k|\]二范式:\[||x||_1=\sqrt{\sum_k{x_k^2}}\]a.norm(k,dim)这个dim,可以不填,不填就是......
  • 【补充】isinstance()issubclass()
    【补充】isinstance()/issubclass()isinstance()函数和issubclass()函数是Python中的两个内置函数用于判断对象与类之间的关系。【1】isinstance()isinstance()函数用于检查一个对象是否是一个类的实例。它接受两个参数第一个参数是待检查的对象第二个参数是类或类型。......
  • 【补充】docker-compose部署Yapi
    【补充】docker-compose部署Yapi参考博客:docker-compose部署Yapi-知乎(zhihu.com)【1】了解DockerCompose:DockerCompose是Docker官方推出的一个用于定义和运行多个Docker容器的工具。通过使用DockerCompose,你可以使用一个单独的yaml文件来定义多个相关的服务,然后使......