如何选择 operator == ?

How operator == is selected?

internal static class ChoosingEqOperTest
{
    class A
    {
        public static bool operator ==(A a, A a2) => false;
    }

    class B:A
    {
        public static bool operator ==(B a, B a2) => true;
    }

    static void CheckChosenOper<T>(T x, T x2) where T : A
    {
        Console.WriteLine(x==x2);
    }

    internal static void Do()
    {
        var a  = new A();
        var a2 = new A();
        var b  = new B();
        var b2 = new B();
        CheckChosenOper(a,a2);      // 1. "False"
        CheckChosenOper(b,b2);      // 2. "False" !?
        CheckChosenOper<A>(a,a2);   // 3. "False"
        CheckChosenOper<A>(b, b2);  // 4. "False"
        //CheckChosenOper<B>(a, a2); //Complie Error
        CheckChosenOper<B>(b, b2);  // 5. "False" !?
        Console.WriteLine(a == a2); // 6. "False"
        Console.WriteLine(b == b2); // 7. "True"
        Console.WriteLine(a == b2); // 8. "False"
        Console.WriteLine(b == a2); // 9. "False"
    }
}

一些问题:

A​​) 为什么 #2 和 #5 打印“False”? - 在这些情况下,我希望运算符实现应该取自 B class。

B) 我说得对吗:由于 #8 和 #9 都打印出“False” - Cosen 运算符实现是第一个发现的,所以两种事实参数类型都可以转换为其参数类型?

c) 选择运算符 == 实现的常见规则是什么?

A) 嗯,你的方法 CheckChosenOper 是一个通用方法,它仍然“使用”class A 作为基础 class 并且它自己的 == 运算符甚至如果您有意指定 <B> 类型。似乎运算符在派生的 classes 中没有被覆盖,并且 CheckChosenOper 方法只能使用它已知的内容: class A. 它不能单独使用反射来搜索其他有效的相等运算符。

B) 不,你错了。在#8 和#9 的情况下,编译器有两个选择——将 A 向上转换为 B 并将 B1 与 B2 进行比较,将 B 向下转换为 A 并将 A1 与 A2 进行比较。由于向上转型是一个禁忌,必须通过转型运算符专门完成,编译器会选择简单的 A1==A2。

C) 好吧,如果我要写一个 struct,我肯定会写一个。以及 !=GetHashCodeIEquatable<> 和其他一些内容。但是必须有充分的理由来创建您自己的结构,因为它们的行为不同。

  1. 你在这里使用了静态绑定(静态绑定是默认的,除非你使用dynamic),所以重载解析发生在编译时。因此,表达式 x==x2 必须解析为 A 中的 ==B 中的 ==。它不能同时解决它。如果仔细查看编译器的信息,您会发现它无法将其解析为 B 中的信息,因为它只知道 TA 或其子类。 T 可以是 AB 的兄弟,在这种情况下调用 B 重载根本不起作用。您不能将 A 传递给 B 参数,可以吗?

    您可以通过使用通用方法 dynamic 而不是通用方法来查看预期结果:

     static void CheckChosenOper(dynamic x, dynamic x2)
     {
         Console.WriteLine(x==x2);
     }
    
  2. 编译器必须解析为 A 重载,因为参数之一是 A,并且 As 不能隐式转换为 B, 所以不能传入那个参数.

  3. 这一切都在语言规范中的 Binary operator overload resolution. But most of it follows the same rules as method resolution 部分进行了描述,因此您可能只想阅读它。