为什么操作符的静态重载可以调用同一类的虚方法?

本文关键字:一类 方法 静态 操作符 重载 调用 为什么 | 更新日期: 2023-09-27 18:03:27

我正在探索c#中的相等性,我正在实现以下内容:

public class MyType
{
    public string MyProperty { get; set; }
    public MyType(string myProperty)
    {
        MyProperty = myProperty;
    }
    protected bool Equals(MyType other)
    {
        Console.WriteLine("I'm calling the MyType.Equals override");
        return MyProperty == other.MyProperty;
    }
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((MyType) obj);
    }
    public override int GetHashCode()
    {
        throw new NotImplementedException();
    }
    public static bool operator ==(MyType lhs, MyType rhs)
    {
        return Equals(lhs, rhs);
    }
    public static bool operator !=(MyType lhs, MyType rhs)
    {
        return !(lhs == rhs);
    }
}
class Program
{
    static void Main(string[] args)
    {
        var type1 = new MyType("test");
        var type2 = new MyType("test");
        Console.WriteLine($"type1 == type2 => {type1 == type2}");
        Console.Read();
    }
}

,输出为

我正在调用MyType。=覆盖
type1 == type2 => True

虽然我完全意识到通过这种方式重写相等操作符可能会出现的意外,但我想知道的是为什么有可能最终从静态方法调用实例虚拟方法(MyType类中的受保护bool Equals(MyType other))。

的情况下它看起来不像静态方法

操作符

关键字,但据我所知,它在IL中被翻译为静态方法:

.method public hidebysig specialname static bool  op_Equality(class MyType lhs, class MyType rhs) cil managed

我怀疑魔法发生在物体的某个地方。等于静态方法调用,但我不知道它是如何工作的。能告诉我吗?

为什么操作符的静态重载可以调用同一类的虚方法?

不需要任何魔法。Equals方法在object上是虚拟的,并且您正在重写它-因此当调用((object)lhs).Equals时,当然会调用您的重写方法。这就是你正在调用的静态object.Equals:)如果你在其他任何地方调用Equals,也会发生同样的事情,操作符真的只是静态方法。

没有什么神奇的——你自己也可以实现类似的代码。您使用的Object.Equal(object,object)最终调用left.Equals(right)产生的结果如下:

如果两个对象不代表相同的对象引用,并且都不为空,则调用objA.Equals(objB)并返回结果。这意味着如果objA覆盖了Object. equals (Object)方法,就会调用这个覆盖。

public static bool operator ==(MyType lhs, MyType rhs)
{
    return Equals(lhs, rhs); // calls Object.Equal(object,object)
}

代码大致相当于以下内联实现:

public static bool operator ==(MyType lhs, MyType rhs)
{
    if (lhs == null || lhs == null) 
    {
        // this branch is not executed in your sample
        return RefrenceEquals(lhs,rhs);
    } 
    return lhs.Equal(rhs); // calls override bool Equals(object obj)
    // which in turn calls 
    // bool Equals(MyType other) since both are other is not null
}

这是因为当访问类本身(以及它的派生类)内部的静态成员(字段/方法等)时,可以省略类型标识符。

class A
{
    public static int Number = 0;
    protected static void MA()
    {
        var n = Number;    // var n = A.Number;
    }
}

在派生类中:

class B : A
{
    public static void MB()
    {
        var n = Number;   // var n = A.Number;
        MA();             // A.MA();
    }
}

由于每个类都派生自System.Object,因此可以从每个类访问公共静态方法Object.Equals,省略Object.