使用变量通用委托类型对operator ==进行重载解析

Jep*_*sen 5 c# delegates operator-overloading overload-resolution

==委托类型的两个表达式之间的重载解析的准确规则是什么?

请考虑以下代码(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>'
    }
}
Run Code Online (Sandbox Code Playgroud)

说明:

instance1并且instance2是同一运行时类型的两个独立实例,泛型Action<in T>逆变T.这些情况是截然不同的,但Equals因为它们具有相同的目标.

a1a2是相同的instance1instance2,但由于逆变的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
Run Code Online (Sandbox Code Playgroud)

当前的Visual C#编译器通过简单的检查,如果引用是相同的实现第一个(在IL实际上不叫mscorlib程序方法一样object.ReferenceEquals,但这样会产生相同的结果),而它通过调用实现第二个Delegate.op_Equality方法看起来就像该组件中的"用户定义"操作符一样,即使它是由C#语言规范定义的,因此在规范(?)的意义上也许不是"用户定义的".

请注意,§7.10.8有点令人困惑,因为它说"每个委托类型隐式提供以下预定义比较运算符[s]",然后为运算符提供(System.Delegate, System.Delegate)签名.那只是一个运算符,而不是"每个"委托类型的运算符?这对我的问题似乎很重要.

三个第一次WriteLineFalse,True并且True分别给出我上面所述的内容并不奇怪.

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

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

当然,和之间没有隐含的转换,但为什么这是相关的?如果我创建自己的包含用户定义的类,并且我创建了两个不相关的派生类,那么我的运算符仍将被使用(左右操作数不能互相转换,但都可以转换为).Action<string>Action<Uri>MyBaseClassoperator ==(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>'
    }
}
Run Code Online (Sandbox Code Playgroud)

上述有关我的问题的一个问题是,其中在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>'
Run Code Online (Sandbox Code Playgroud)

我可以看到编译器发现没有类型可以从两者继承string而且Uri(不像对ICloneableIConvertible),所以这(如果它是合法的)只有true在两个变量都是null,但它在哪里说我不是允许这样做?在这种情况下,无论编译器是选择operator ==(object, object)还是operator ==(Delegate, Delegate)因为,正如我所说,它归结为检查两者是否都是空引用,并且两个重载都以相同的方式执行.

小智 0

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

因为它是编译器的唯一选择:-)

Cannot apply operator '==' to operands of type 'System.Func<System.ICloneable>' and 'System.Func<System.IConvertable>'
Run Code Online (Sandbox Code Playgroud)

候选人是:

bool==(System.Delegate, System.Delegate) 
bool==(System.Func<System.ICloneable>, System.Func<System.ICloneable>)
bool==(System.Func<System.IConvertable>, System.Func<System.IConvertable>)
Run Code Online (Sandbox Code Playgroud)

因此,使用您的 (object, object) 是编译器找到的最佳选择。

动作也一样

Cannot apply operator '==' to operands of type 'System.Action<string>' and 'System.Action<System.Uri>'
Run Code Online (Sandbox Code Playgroud)

候选人是:

bool==(System.Delegate, System.Delegate) 
bool==(System.Action<string>, System.Action<string>)
bool==(System.Action<System.Uri>, System.Action<System.Uri>)
Run Code Online (Sandbox Code Playgroud)