2016-08-23 8 views
0

Я ковыряться с равенством в 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.Equals переопределить
type1 = = type2 => True

, в то время как я полностью осведомлен о возможных неожиданностях, которые могут возникнуть в результате переопределения оператора равенства таким образом, что я хотел бы знать, почему это можно вызвать вызов виртуального метода экземпляра (защищенный bool Equals (MyType other) в классе MyType) из статического метода.
Хорошо, это не выглядит как статический метод данного оператору

ключевое слово, но, насколько я могу сказать, что переводится как статический метод в IL:

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

Я подозреваю, что магия происходит где-то в объекте. Статический метод вызова Equals, но я не знаю, как это работает. Уход, чтобы пролить свет?

+0

Включение точки останова в этот метод, отладка кода и проверка стека вызовов - довольно простая отладка и позволит вам увидеть серию вызовов, которые задействованы. –

+0

@Damien_The_Unbeliever мое глупое сомнение не имело никакого отношения к стеку вызовов, но по какой-то причине оно просто не нажимало на меня, что статический метод может вызывать методы экземпляра других объектов. Я не знаю точно, почему у меня возникло это сомнение в этом конкретном случае, это не имеет никакого смысла. –

ответ

1

Нет волшебства - и вы можете реализовать похожий код самостоятельно, просто отлично. 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 
} 
1

Там нет необходимости в какой-либо магии. Метод Equals является виртуальным на object, и вы его переопределяете - поэтому при вызове ((object)lhs).Equals, конечно, вы вызываете метод overriden. И это то, что вызывает статический object.Equals :). То же самое происходит, если вы вызываете Equals в любом месте, операторы действительно являются только статическими методами.

1

Это связано с тем, что при доступе к статическому члену (поле/метод/etc) внутри самого класса (а также его производных классов) идентификатор типа можно опустить.

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. опущена.