==
重载决议的精确规则是什么?在委托(delegate)类型的两个表达式之间?
考虑以下代码(需要 using System;
的地方):
static class ProgramA
{
static void TargetMethod(object obj)
{
}
static void Main()
{
Action<object> instance1 = TargetMethod;
Action<object> instance2 = TargetMethod;
Action<string> a1 = instance1;
Action<Uri> a2 = instance2;
Console.WriteLine((object)a1 == (object)a2);
Console.WriteLine((Delegate)a1 == (Delegate)a2);
Console.WriteLine((Action<object>)a1 == (Action<object>)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<string>'
}
}
解释:
instance1
和 instance2
是相同运行时类型的两个独立实例,通用 Action<in T>
在 T
中是逆变 .这些实例是不同的,但是 Equals
因为他们有相同的目标。
a1
和 a2
与 instance1
相同和 instance2
, 但由于 Action<in T>
的逆变Action<object>
存在隐式 引用转换每Action<string>
和 Action<System.Uri>
.
现在,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
method它看起来像是该程序集中的“用户定义”运算符,即使它是由 C# 语言规范定义的,所以在规范的意义上可能不是“用户定义的”(?)。
请注意,§7.10.8 有点令人困惑,因为它说“每个委托(delegate)类型隐式提供以下预定义比较运算符[s]”,然后为运算符提供 (System.Delegate, System.Delegate)
签名。这只是一个 运算符,而不是“每个”委托(delegate)类型的一个运算符?这对我的问题来说似乎很重要。
三个第一也就不足为奇了WriteLine
写False
, True
和 True
,分别根据我上面所说的。
问题:但是为什么第四个WriteLine
导致(object, object)
正在使用过载?
确实存在从 Action<>
的隐式引用转换(或任何其他委托(delegate)类型)到 System.Delegate
,那为什么不能在这里使用呢?过载解决方案应该优先于 (object, object)
选项。
当然,Action<string>
之间没有隐式 转换。和 Action<Uri>
,但为什么相关?如果我创建自己的类 MyBaseClass
包含用户定义的 operator ==(MyBaseClass x, MyBaseClass y)
我创建了两个不相关的派生类,然后是我的 ==
运算符仍将被使用(左右操作数不能相互转换,但都可以转换为 MyBaseClass
)。
为了完整起见,这里是使用协方差 ( Func<out TResult>
) 而不是逆变的类似示例:
static class ProgramF
{
static string TargetMethod()
{
return "dummy";
}
static void Main()
{
Func<string> instance1 = TargetMethod;
Func<string> instance2 = TargetMethod;
Func<ICloneable> f1 = instance1;
Func<IConvertible> f2 = instance2;
Console.WriteLine((object)f1 == (object)f2);
Console.WriteLine((Delegate)f1 == (Delegate)f2);
Console.WriteLine((Func<string>)f1 == (Func<string>)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<System.ICloneable>'
}
}
与我上面的问题相关的一个问题是,C# 语言规范中哪里说这是非法的:
Func<string> g1 = ...;
Func<Uri> g2 = ...;
Console.WriteLine(g1 == g2); // error CS0019: Operator '==' cannot be applied to operands of type 'System.Func<string>' and 'System.Func<System.Uri>'
我可以看到编译器发现没有类型可以同时继承自 string
和 Uri
(不像这对 ICloneable
和 IConvertible
),所以这个(如果它是合法的)只能变成 true
如果两个变量都是 null
,但它在哪里说不允许我这样做?在这种情况下,编译器是否选择 operator ==(object, object)
并不重要。或 operator ==(Delegate, Delegate)
因为,正如我所说,它归结为检查两者是否都是空引用,并且两个重载都以相同的方式进行。
最佳答案
Question: But why does the fourth WriteLine lead to the (object, object) overload being used?
因为它是编译器的唯一选择:-)
Cannot apply operator '==' to operands of type 'System.Func<System.ICloneable>' and 'System.Func<System.IConvertable>'
候选人是:
bool==(System.Delegate, System.Delegate)
bool==(System.Func<System.ICloneable>, System.Func<System.ICloneable>)
bool==(System.Func<System.IConvertable>, System.Func<System.IConvertable>)
所以使用你的(对象,对象)是编译器找到的最佳选择。
同样的 Action
Cannot apply operator '==' to operands of type 'System.Action<string>' and 'System.Action<System.Uri>'
候选人是:
bool==(System.Delegate, System.Delegate)
bool==(System.Action<string>, System.Action<string>)
bool==(System.Action<System.Uri>, System.Action<System.Uri>)
关于c# - 使用变体通用委托(delegate)类型对运算符 == 的重载解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22408165/