Общая реализация равенства для производных классов

Я хочу, чтобы объекты, производные от определенного класса A, также каким-то образом производили реализацию Equals(A other), которая делала бы следующее: если типы this и other различны, возвращать false, иначе возвращать this.value == other.value.

Моя попытка выглядит так:

public class A<T> : IEquatable<A<T>>
    where T: A<T>
{
    protected string Value { get; }
    public A(string value)
    {
        Value = value;
    }

    public bool Equals(A<T> other)
    {
        var concrete = other as T;
        if (concrete == null)
        {
            return false;
        }

        return concrete.Value == Value;
    }
}

public class B : A<B>
{
    public B(string value)
        : base(value)
    {

    }
}

public class C : A<C>
{
    public C(string value)
        : base(value)
    {

    }
}

class Program
{
    static void Main(string[] args)
    {
        var b1 = new B("val");
        var b2 = new B("val");
        var c = new C("val");

        Console.WriteLine(b1.Equals(b1));
        Console.WriteLine(b1.Equals(b2));
        Console.WriteLine(b2.Equals(b1));
        Console.WriteLine(b1.Equals(c));
        Console.WriteLine(b2.Equals(c));
        Console.WriteLine(c.Equals(b1));
        Console.WriteLine(c.Equals(b2));
    }
}

Это работает нормально, пока мы не получим больше:

public class D : C
{
    public D(string value)
        : base(value)
    {

    }
}

потом ломается:

        var d = new D("val");
        Console.WriteLine(d.Equals(c)); // prints "True"

и теперь я застрял. Как заставить это работать? Приемлемы как фиксация реализации для работы с более чем одним уровнем наследования, так и предотвращение более чем одного уровня наследования.

Я понимаю, однако, что мне просто нужно объявить всех потомков первого уровня A<T> запечатанными, но это последнее средство, если его нельзя каким-то образом принудительно применить (поэтому незапечатанные потомки A<T> вызовут ошибку компиляции). Или, может быть, мой подход неверен полностью?


person n0rd    schedule 28.04.2018    source источник


Ответы (1)


Это все потому, что оператор as может без проблем преобразовывать подклассы в суперклассы.

Что вы хотите сделать, так это проверить типы и посмотреть, равны ли они:

if (this.GetType() == other.GetType()) {
    return false;
}

Этот вопрос связан с поведением GetType, typeof и is , который работает аналогично as.

person Sweeper    schedule 28.04.2018
comment
У меня было подозрение, что я преувеличиваю. Это работает, но я немного подожду других ответов, прежде чем принять. - person n0rd; 28.04.2018
comment
Тогда нет необходимости делать класс A универсальным. - person Cheng Chen; 28.04.2018
comment
@DannyChen Из того, что показал OP, A вообще не обязательно должен быть универсальным, но это не может быть полный код OP, поэтому я предположил, что OP использовал общий параметр где-то еще. - person Sweeper; 28.04.2018