3

もう一度平等について話し合っていて、偶然見つけましたEqualityComparer<T>.Default.Equals()。私はこのメソッドをobject.Equals().
今、私はひどく間違っていたと思います。

object.Equals()実装されている場合は呼び出しますがEquals()、正しいポリモーフィック動作を提供するオーバーライド可能なインスタンス メソッドを使用します。EqualityComparer<T>.Default.Equals()IEquatable<T>.Equals()

この小さなプログラムを考えてみましょう:

public class Class1 : IEquatable<Class1>
{
    public int Prop1 { get; set; }

    public bool Equals(Class1 other)
    {
        if (other == null)
            return false;

        return Prop1 == other.Prop1;
    }

    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
        {
            return false;
        }

        return Equals(obj as Class1);
    }
}

public class Class2 : Class1, IEquatable<Class2>
{
    public int Prop1 { get; set; }
    public int Prop2 { get; set; }

    public bool Equals(Class2 other)
    {
        if (other == null)
            return false;

        return Prop1 == other.Prop1 && Prop2 == other.Prop2;
    }

    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
        {
            return false;
        }

        return Equals(obj as Class2);
    }
}


class Program
{
    static void Main(string[] args)
    {
        var c1 = new Class1 {Prop1 = 10};
        var c2 = new Class2 {Prop1 = 10, Prop2 = 5};
        var c3 = new Class2 {Prop1 = 10, Prop2 = 15};

        Console.WriteLine("Object.Equals()");
        Console.WriteLine("C1=C2 {0}",Equals(c1,c2));
        Console.WriteLine("C2=C1 {0}",Equals(c2, c1));
        Console.WriteLine("C2=C3 {0}",Equals(c2, c3));
        Console.WriteLine("C3=C2 {0}", Equals(c3, c2));

        var dec1 = EqualityComparer<Class1>.Default;

        Console.WriteLine();
        Console.WriteLine("EqualityComparer<Class1>.Default.Equals");
        Console.WriteLine("C1=C2 {0}", dec1.Equals(c1, c2));
        Console.WriteLine("C2=C1 {0}", dec1.Equals(c2, c1));
        Console.WriteLine("C2=C3 {0} BUG?", dec1.Equals(c2, c3));
        Console.WriteLine("C3=C2 {0} BUG?", dec1.Equals(c3, c2));

        Console.ReadKey();
    }
}

これは、平等のセマンティクスに矛盾をもたらしやすいことを示しています。

Object.Equals()
C1=C2 False
C2=C1 False
C2=C3 False
C3=C2 False

EqualityComparer<Class1>.Default.Equals
C1=C2 False
C2=C1 False
C2=C3 True バグ?
C3=C2 本当のバグ?

ただし、 MSDN ドキュメントでは次のことを推奨しています。

実装者への注意 Equals を実装する場合は、Object.Equals(Object) および GetHashCode の基本クラスの実装もオーバーライドして、それらの動作が IEquatable<T>.Equals メソッドの動作と一致するようにする必要があります。Object.Equals(Object) をオーバーライドする場合、オーバーライドされた実装は、クラスの静的 Equals(System.Object, System.Object) メソッドの呼び出しでも呼び出されます。さらに、op_Equality および op_Inequality 演算子をオーバーロードする必要があります。これにより、等しいかどうかのすべてのテストで一貫した結果が返されることが保証されます。これは例が示しています。

この瞬間からIEquatable<T>、参照型を実装する理由がわかりません。意味があるときは誰か教えてもらえますか?型を (基本型として) 別の方法で見た場合、異なる等価動作を一貫性のないものとして扱うべきでしょうか?

4

2 に答える 2

2

善悪を問わず、私が基本クラスと派生クラスを実装する傾向がある方法を次に示しEquals(Object)ますIEquatable<T>.Equals(T)

public class Class1 : IEquatable<Class1>
{    
    public sealed override bool Equals(object obj)
    {
        return Equals(obj as Class1);
    }

    public virtual bool Equals(Class1 obj)
    {
        if(ReferenceEquals(obj, null))
            return false;

        // Some property checking
    }
}

public class Class2 : Class1, IEquatable<Class2>
{
    public sealed override bool Equals(Class1 obj)
    {
        return Equals(obj as Class2);
    }

    public virtual bool Equals(Class2 obj)
    {
        if(!base.Equals(obj))
            return false;

        // Some more property checking
    }
}

public class Class3 : Class2, IEquatable<Class3>
{
    public sealed override bool Equals(Class2 obj)
    {
        return Equals(obj as Class3);
    }

    public virtual bool Equals(Class3 obj)
    {
        if(!base.Equals(obj))
            return false;

        // Some more property checking
    }
}

参照型の場合、実装の利点IEquatable<T>はわずかです。 type のインスタンスが 2 つある場合はT、 を直接呼び出すことができますT.Equals(T)。代わりに、T.Equals(Object)後でパラメーターに対して型チェックを実行する必要があります。

の主な目的はIEquatable<T>、インスタンスのボックス化にオーバーヘッドがある値型です。

于 2013-11-26T14:48:29.053 に答える