c# - 使用变体通用委托(delegate)类型对运算符 == 的重载解析

标签 c# delegates operator-overloading overload-resolution

== 重载决议的精确规则是什么?在委托(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>'
    }
}

解释:

instance1instance2是相同运行时类型的两个独立实例,通用 Action<in T>T 中是逆变 .这些实例是不同的,但是 Equals因为他们有相同的目标。

a1a2instance1 相同和 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)类型的一个运算符?这对我的问题来说似乎很重要。

三个第一也就不足为奇了WriteLineFalse , TrueTrue ,分别根据我上面所说的。

问题:但是为什么第四个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>'

我可以看到编译器发现没有类型可以同时继承自 stringUri (不像这对 ICloneableIConvertible ),所以这个(如果它是合法的)只能变成 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/

相关文章:

c# - 如何将点序列转换为范围序列?

java - Obj-c 委托(delegate)模型 - 在 Java 中?

Swift 委托(delegate)协议(protocol)无法防止保留周期问题

c++ - 重载运算符 '>>' 的使用不明确(操作数类型为 'istream'(又名 'basic_istream<char>')和 'MyIncreEx')

c++ - BigInteger 类实现重载运算符 '+'

C#控制台应用程序,退出缓慢

c# - 在循环中拦截变量名

c# - C#删除临时文件夹中的文件

C# 函数委托(delegate)

c++ - 排序 vector 时运算符重载的编译错误