当前位置: 首页 > 编程语言 > C#

隐式方法组转换问题(下)分享

时间:2023-04-10 23:00:44 C#

隐式方法组转换问题(下)从这个问题上进行了简化,摆脱了LinqPad(不密)可能带来的影响,这样一个简单的Console应用:公共类程序{staticvoidM(){}staticvoidMain(string[]args){Actiona=newAction(M);代表b=新动作(M);Console.WriteLine(a==b);//这里得到FalseConsole.Read();“假”结果来自上述代码的CIL中的运算符ceq(有关详细信息,请参见原始问题)。所以我的问题是:(1)为什么==被转换为ceq而不是调用DelegateEquals?在这里,我不关心Delegate和Action之间的(取消)包装。最后,在评估a==b之后,a是Action类型,b是Delegate。来自规范:7.3.4二元运算符重载决议形式为xopy的运算,其中op是可重载的二元运算符,x是类型X的表达式,y是类型Y的表达式,如下处理:?确定由运算符op(x,y)的X和Y提供的候选用户自定义运算符集。该集合由X提供的候选运算符和Y提供的候选运算符的并集组成,每个运算符都使用§7.3.5的规则确定。如果X和Y是同一类型,或者如果X和Y派生自公共基类型,则共享候选运算符在组合集中只出现一次。?如果候选用户定义运算符集不为空,则这将成为该操作的候选运算符集。否则,预定义的二元运算符op实现(包括它们的提升形式)成为操作的候选运算符集。给定运算符的预定义实现在运算符的描述(§7.7到§7.12)中指定。?将第7.5.3节的重载决议规则应用于候选运算符集,以根据参数列表(x,y)选择最佳运算符,这将是重载决议过程的结果。如果重载决策无法选择单个最佳运算符,则会发生绑定时错误。7.3.5候选用户定义运算符给定类型T和运算符op(A),其中op是可重载运算符,A是参数列表,T是运算符op(A)的候选用户定义运算符)运算符集按如下方式确定:?确定类型T0。如果T是可空类型,则T0是其基础类型,否则T0等于T。?对于T0中的所有运算符op声明和此类运算符的所有提升形式,如果对于参数列表A,至少有一个运算符适用(§7.5.3.1),则候选运营商集由所有此类适用运营商组成。T0。?否则,如果T0是一个对象,则候选运算符的集合为空。?否则,T0提供的候选运算符集是T0的直接基类或T0的有效基类(如果T0是类型参数)提供的候选运算符集。从规范来看,a和b具有相同的基类Delegate,显然Delegate==中定义的运算符规则应该适用于此(operator==本质上调用Delegate.Equals)。但是现在看起来用户自定义运算符的候选列表是空的,最后应用了Object==。(2)FCL代码是否应该符合C#语言规范?如果不是,我的第一个问题就毫无意义,因为有些东西是特殊对待的。那么我们就可以回答所有这些问题,“哦,这是对FCL的特殊处理,他们可以做我们做不到的事情。规范是针对外部程序员的,别傻了”。有两种类型的运算符:用户定义的运算符和预定义的运算符。第7.3.5节,“候选用户定义的运算符”不适用于预定义的运算符。例如,小数运算符在反编译器中看起来像用户定义的运算符,但C#将它们视为预定义运算符并对其应用数值提升(数值提升不适用于用户定义的运算符)。Section7.10.8,"Delegatingequalityoperators"将operator==(Delegate,Delegate)定义为预定义运算符,所以我认为所有关于用户定义运算符的规则都不适用于这个运算符(虽然这不是在这种情况下是规范中100%明确,只要是用户自定义的operator(),预定义的operator就不会被应用。每个委托类型都隐式提供了以下预定义的比较运算符:booloperator==(System.Delegatex,System.Delegatey);布尔运算符!=(System.Delegatex,System.Delegatey);但是System.Delegate本身不被视为委托类型,因此重载决议的唯一候选operator==(object,object)是operator==(object,object)。编译器与代表的工作方式截然不同且不同寻常。有很多隐式处理。请注意,本指南中“公共基类型”的规则适用于“用户定义的运算符”。代表是内部的和系统的。例如,你可以这样写Actiona=M;而不是Actiona=newAction(M);.你可以加一个+=M;在那之后。第一次检查CIL中发生的事情很有趣。更多:比较代表是危险且不重要的。每个委托实际上是一个多播委托。您可以将多个函数指针添加到同一个委托。表示是否[L();M();N();][L();M();N();][L();M();N();]等于委托[M();]?函数指针包含类实例(例如方法)。[aM();]等于[bM();]吗?所有这一切都取决于具体情况,比较实现需要遍历调用列表。从公共基类型的委托继承是隐式的,您可能会在另一种情况下遇到这个问题,例如泛型约束:您不能将委托指定为泛型参数T的约束。这里编译器明确拒绝这一点。创建自己的类也是如此,继承自Delegate。这是两个问题的答案——“委托”不是纯粹的FCL,而是与编译器紧密结合。如果您真的想要Microsoft的委托比较器行为-只需调用Equals(a,b)显式警告CS0253:Possibleunexpectedreferencecomparison;要进行值比较,请将“System.Action”放在C#代码的右侧警告上。不要忽略那个警告,C#团队很清楚他们为这个比较生成的代码是出乎意料的。他们不必生成代码,他们可以轻松地做你期望的事情。像这样的代码:ModuleModule1SubM()EndSubSubMain()Dima=NewAction(AddressOfM)Dimb=DirectCast(NewAction(AddressOfM),[Delegate])Console.WriteLine(a=b)''在这里得到TrueConsole.Read()EndSubEndModule生成几乎相同的MSIL,除了你得到的而不是ceq:IL_001d:callbool[mscorlib]System.Delegate::op_Equality(class[mscorlib]System.Delegate,class[mscorlib]System.Delegate)做你想让C#代码做的事。这是VB.NET代码,以防您不认识它。否则,微软保留两种主要托管语言的原因,尽管它们具有非常相似的功能。但是具有非常不同的可用性选项。每当有不止一种生成代码的方法时,C#团队总是选择性能,VB.NET团队也是如此。性能当然是关键,比较委托对象是昂贵的。这些规则在Ecma-335第II.14.6.1节中指定。但是你可以自己推理出来,有很多检查要做。它需要检查委托目标对象是否兼容。对于每个参数,它必须检查该值是否可转换。C#团队不想要隐藏费用。不,你会收到警告,提醒你他们做出了不直观的选择。.这里的关键是==运算符和Delegate类型的Equals方法是两个不同的东西。对于引用类型,==检查两个引用是否指向同一个对象,除非==运算符被重写(请参阅:==运算符(C#))。由于你创建的是两个不同的Action对象,即使它们在内部调用了相同的方法,但它们在内存中的不同位置是不同的对象,并且不是值类型或字符串类型,所以==在这种情况下是ReferenceEquals,而不是CallsDelegate.Equals方法,该方法已被覆盖以查看两个对象是否执行相同的操作。对于字符串以外的引用类型,这是==或Equals的默认行为。以上就是《C#学习教程:隐式方法组转换问题(二)》分享的全部内容。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: