使用变量generics委托类型对operator ==进行重载解析
在两个委托类型表达式之间使用==
进行重载解析的精确规则是什么?
请考虑以下代码(需要using System;
):
static class ProgramA { static void TargetMethod(object obj) { } static void Main() { Action instance1 = TargetMethod; Action instance2 = TargetMethod; Action a1 = instance1; Action a2 = instance2; Console.WriteLine((object)a1 == (object)a2); Console.WriteLine((Delegate)a1 == (Delegate)a2); Console.WriteLine((Action)a1 == (Action)a2); Console.WriteLine(a1 == a2); // warning CS0253: Possible unintended reference comparison; to get a value comparison, cast the right hand side to type 'System.Action' } }
说明:
instance1
和instance2
是同一运行时类型的两个独立实例,通用Action
中是逆变的。 这些实例是截然不同的,但是Equals
因为它们具有相同的目标。
a1
和a2
与instance1
和instance2
相同,但由于Action
的逆转,从Action
到Action
和Action
每一个都存在隐式引用转换。
现在,C#语言规范具有(以及其他重载)这些operator ==
:
bool operator ==(object x, object y); // §7.10.6 bool operator ==(System.Delegate x, System.Delegate y); // §7.10.8
当前的Visual C#编译器通过简单地检查引用是否相同来实现第一个(IL实际上没有像object.ReferenceEquals
那样调用mscorlib方法,但是会产生相同的结果),而它通过调用实现第二个Delegate.op_Equality
方法在该程序集中看起来像一个“用户定义”的运算符,即使它是由C#语言规范定义的,因此在规范(?)的意义上也许不是“用户定义的”。
请注意,§7.10.8有点令人困惑,因为它说“每个委托类型隐式提供以下预定义比较运算符[s]” ,然后为运算符提供(System.Delegate, System.Delegate)
签名。 那只是一个运算符,而不是“每个”委托类型的运算符? 这对我的问题似乎很重要。
根据我上面所说的,三个第一个WriteLine
写入False
, True
和True
并不奇怪。
问题:但为什么第四个WriteLine
导致使用(object, object)
重载?
确实存在从Action
(或任何其他委托类型)到System.Delegate
的隐式引用转换,那么为什么不能在这里使用它呢? 重载决策应优先于(object, object)
选项。
当然, Action
和Action
之间没有隐式转换,但为什么这有关系呢? 如果我创建自己的类MyBaseClass
包含用户定义的operator ==(MyBaseClass x, MyBaseClass y)
并且我创建了两个不相关的派生类,那么仍将使用我的==
运算符(左右操作数不能相互转换但是两者都可以转换为MyBaseClass
)。
为了完整起见,这里是协方差 ( Func
)而不是逆变的类似例子:
static class ProgramF { static string TargetMethod() { return "dummy"; } static void Main() { Func instance1 = TargetMethod; Func instance2 = TargetMethod; Func f1 = instance1; Func f2 = instance2; Console.WriteLine((object)f1 == (object)f2); Console.WriteLine((Delegate)f1 == (Delegate)f2); Console.WriteLine((Func)f1 == (Func)f2); Console.WriteLine(f1 == f2); // warning CS0253: Possible unintended reference comparison; to get a value comparison, cast the right hand side to type 'System.Func' } }
与我上面的问题相关的一个问题是,在C#语言规范中它表示这是非法的:
Func g1 = ...; Func g2 = ...; Console.WriteLine(g1 == g2); // error CS0019: Operator '==' cannot be applied to operands of type 'System.Func' and 'System.Func'
我可以看到编译器发现没有类型可以从string
和Uri
inheritance(不像ICloneable
和IConvertible
对),所以这个(如果它是合法的)只有在两个变量都为null
才会变为true
,但是它说我不被允许这样做? 在这种情况下,如果编译器选择operator ==(object, object)
或operator ==(Delegate, Delegate)
并不重要operator ==(Delegate, Delegate)
因为正如我所说,它归结为检查两者是否都是空引用,并且两个重载都是这样做的以同样的方式。
问题:但为什么第四个WriteLine导致使用(对象,对象)重载?
因为它是编译器的唯一选择:-)
Cannot apply operator '==' to operands of type 'System.Func' and 'System.Func'
候选人是:
bool==(System.Delegate, System.Delegate) bool==(System.Func, System.Func) bool==(System.Func, System.Func)
所以使用你的(对象,对象)是编译器找到的最佳选择。
同样的行动
Cannot apply operator '==' to operands of type 'System.Action' and 'System.Action'
候选人是:
bool==(System.Delegate, System.Delegate) bool==(System.Action, System.Action ) bool==(System.Action, System.Action)