使用变量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' } } 

说明:

instance1instance2是同一运行时类型的两个独立实例,通用Action中是逆变的。 这些实例是截然不同的,但是Equals因为它们具有相同的目标。

a1a2instance1instance2相同,但由于Action的逆转,从ActionActionAction每一个都存在隐式引用转换。

现在,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写入FalseTrueTrue并不奇怪。

问题:但为什么第四个WriteLine导致使用(object, object)重载?

确实存在从Action (或任何其他委托类型)到System.Delegate的隐式引用转换,那么为什么不能在这里使用它呢? 重载决策应优先于(object, object)选项。

当然, ActionAction之间没有隐式转换,但为什么这有关系呢? 如果我创建自己的类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' 

我可以看到编译器发现没有类型可以从stringUriinheritance(不像ICloneableIConvertible对),所以这个(如果它是合法的)只有在两个变量都为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)