web-dev-qa-db-ja.com

カスタム型にEqualsを最適に実装する方法は?

Point2クラスについて、次のEqualsについて説明します。

public override bool Equals ( object obj )

public bool Equals ( Point2 obj )

これは、効果的なC#3に示されているものです。

public override bool Equals ( object obj )
{
    // STEP 1: Check for null
    if ( obj == null )
    {
        return false;
    }

    // STEP 3: equivalent data types
    if ( this.GetType ( ) != obj.GetType ( ) )
    {
        return false;
    }
    return Equals ( ( Point2 ) obj );
}

public bool Equals ( Point2 obj )
{
    // STEP 1: Check for null if nullable (e.g., a reference type)
    if ( obj == null )
    {
        return false;
    }
    // STEP 2: Check for ReferenceEquals if this is a reference type
    if ( ReferenceEquals ( this, obj ) )
    {
        return true;
    }
    // STEP 4: Possibly check for equivalent hash codes
    if ( this.GetHashCode ( ) != obj.GetHashCode ( ) )
    {
        return false;
    }
    // STEP 5: Check base.Equals if base overrides Equals()
    System.Diagnostics.Debug.Assert (
        base.GetType ( ) != typeof ( object ) );

    if ( !base.Equals ( obj ) )
    {
        return false;
    }

    // STEP 6: Compare identifying fields for equality.
    return ( ( this.X.Equals ( obj.X ) ) && ( this.Y.Equals ( obj.Y ) ) );
}
35
Joan Venge

MSDNのガイドライン の完全なセットもあります。あなたはそれらをよく読むべきです、それはトリッキーで重要です。

私が最も役に立ったと思ういくつかの点:

  • 値型にはIDがないため、_struct Point_では通常、メンバーごとに比較を行います。

  • 参照タイプには通常IDがあるため、Equalsテストは通常​​ReferenceEqualsで停止します(デフォルト、オーバーライドする必要はありません)。ただし、文字列や_class Point2_などの例外があり、オブジェクトに有用なIDがない場合、Equalityメンバーをオーバーライドして、独自のセマンティクス。そのような状況では、ガイドラインに従って、最初にnullおよびその他のタイプのケースを通過してください。

  • また、GethashCode()と_operator==_の同期を維持することには十分な理由があります。

27
Henk Holterman

Objを受け取るものでは、objのタイプがPoint2の場合、タイプ固有のEqualsを呼び出します。タイプ固有のEquals内で、すべてのメンバーが同じ値を持っていることを確認してください。

public override bool Equals ( object obj )
{
   return Equals(obj as Point2);
}

public bool Equals ( Point2 obj )
{
   return obj != null && obj.X == this.X && obj.Y == this.Y ... 
   // Or whatever you think qualifies as the objects being equal.
}

GetHashCodeもオーバーライドして、「等しい」オブジェクトが同じハッシュコードを持つようにする必要があります。

36

私が使ってきたテクニックは次のとおりです。 2つの値ではなく、1つのプロパティ(Id)のみに基づいて比較していることに注意してください。必要に応じて調整

using System;
namespace MyNameSpace
{
    public class DomainEntity
    {
        public virtual int Id { get; set; }

        public override bool Equals(object other)
        {
            return Equals(other as DomainEntity);
        }

        public virtual bool Equals(DomainEntity other)
        {
            if (other == null) { return false; }
            if (object.ReferenceEquals(this, other)) { return true; }
            return this.Id == other.Id;
        }

        public override int GetHashCode()
        {
            return this.Id;
        }

        public static bool operator ==(DomainEntity item1, DomainEntity item2)
        {
            if (object.ReferenceEquals(item1, item2)) { return true; }
            if ((object)item1 == null || (object)item2 == null) { return false; }
            return item1.Id == item2.Id;
        }

        public static bool operator !=(DomainEntity item1, DomainEntity item2)
        {
            return !(item1 == item2);
        }
    }
}
11
user1325543
  • IDの意味を定義します。参照IDの場合、デフォルトの継承された等価物が機能します。
  • 値のタイプ(および値のID)を定義する必要がある場合。
  • クラス型の場合は、値のセマンティクスがある場合に定義します。

both Equals(object)をオーバーライドし、Equals(MyType)を定義したい場合があります。後者はボクシングを回避するためです。そして、等価演算子をオーバーライドします。

.NET Frameworkガイドラインブック(第2版)には、より多くの記事があります。

2
Richard

C#7とis type varnameパターン( https://docs.Microsoft.com/en-us/dotnet/csharp/language-reference/keywords/is#type )を使用すると、次のいずれかのアプローチを使用してnullと型チェックを処理するクリーンなEquals(object)

// using Equals(point)
public override bool Equals(object obj) =>
    (obj is Point other) && this.Equals(other);

// using the == operator
public override bool Equals(object obj) =>
    (obj is Point other) && this == other;

明らかに、少なくとも次のいずれかを実装する必要があります:

public bool Equals(Point2 other);
public static bool operator == (Point2 lhs, Point2 rhs);
1
Brad Bodily

Fodyプラグイン Equals.Fody もあり、Equals()とGetHashCode()を自動的に生成します

1
mamuesstack

嘘ダニエルLは言った、

public override bool Equals(object obj) {
    Point2 point = obj as Point2; // Point2? if Point2 is a struct
    return point != null && this.Equals(point);
}

public bool Equals(Point2 point) {
    ...
}
0
configurator
public override bool Equals ( object obj )
{
   // struct
   return obj  is Point2 && Equals (  ( Point2 ) value );
   // class
   //return Equals ( obj as Point2 );
}

public bool Equals ( Point2 obj )
0
baretta

他の数人がすでに投稿したフォームのわずかな変形...

using System;
...
public override bool Equals ( object obj ) {
   return Equals(obj as SomeClass);
}

public bool Equals ( SomeClass someInstance ) {
    return Object.ReferenceEquals( this, someInstance ) 
        || ( !Object.ReferenceEquals( someInstance, null ) 
            && this.Value == someInstance.Value );
}

public static bool operator ==( SomeClass lhs, SomeClass rhs ) {
    if( Object.ReferenceEquals( lhs, null ) ) {
        return Object.ReferenceEquals( rhs, null );
    }
    return lhs.Equals( rhs );
    //OR
    return Object.ReferenceEquals( lhs, rhs )
            || ( !Object.ReferenceEquals( lhs, null ) 
                && !Object.ReferenceEquals( rhs, null )
                && lhs.Value == rhs.Value );
}

public static bool operator !=( SomeClass lhs, SomeClass rhs ) {
    return !( lhs == rhs );
    // OR
    return ( Object.ReferenceEquals( lhs, null ) || !lhs.Equals( rhs ) )
            && !Object.ReferenceEquals( lhs, rhs );
}

演算子==を実装してEqualsを使用して値の比較ロジックの重複を回避する方法を見つけようとしています...冗長なテスト(ReferenceEqualsが同じパラメーターで呼び出されます)または不要なテスト(これはインスタンスでnullにすることはできません)なしで。メソッド)および明示的な条件(「ifs」)なし。便利なものよりも、心を引き付けるもの。

私が考えることができる最も近いのはこれですが、それはfeelsのように、余分なメソッドなしで可能であるべきです:)

public bool Equals ( SomeClass someInstance ) {
    return Object.ReferenceEquals( this, someInstance ) 
        || (!Object.ReferenceEquals( someInstance, null ) && EqualsNonNullInstance( someInstance );
}

public static bool operator ==( SomeClass lhs, SomeClass rhs ) {
    return Object.ReferenceEquals( lhs, rhs ) 
    || ( !Object.ReferenceEquals( lhs, null ) && !Object.ReferenceEquals( rhs, null ) && lhs.EqualsNonNullInstance( rhs ) );
}

//super fragile method which returns logical non-sense
protected virtual bool EqualsNonNullInstance ( SomeClass someInstance ) {
    //In practice this would be a more complex method...
    return this.Value == someInstance.Value;
}

退屈でエラーが発生しやすいことを思い出してください(上記のコードにエラーがあることはほぼ間違いありません...等価性チェックを少し単純にするためにType justをサブクラス化したいので、それでもなお失敗しますか? )今後は、すべてのnullチェックを処理し、値の比較を実行するためのデリゲートまたは要求とインターフェイスを受け入れる静的メソッドを作成するだけだと思います(TypeをTypeに実際に変更する唯一の部分)。

比較する必要のあるフィールド/プロパティ/メソッドに属性を追加して、コンパイラー/ランタイムにすべての面倒なものを処理させることができればすばらしいと思います。

また、.Equals(object)がtrueを返すインスタンスやクレイジーなたわごとが発生する可能性のあるすべてのインスタンスについて、GetHashCode()の値が等しいことを確認してください。

0
Steazy

Equalsをオーバーライドする簡単で最良の方法は次のようになります。

public class Person
    {
        public int Age { get; set; }
        public string Name { get; set; }

        public override bool Equals(object other)
        {
            Person otherItem = other as Person;

            if (otherItem == null)
                return false;

            return Age == otherItem.Age && Name == otherItem.Name;
        }
        public override int GetHashCode()
        {
            int hash = 13;
            hash = (hash * 7) + Age.GetHashCode();
            hash = (hash * 7) + Name.GetHashCode();
            return hash;
        }
    }

GetHashCodeメソッドをオーバーライドして、ハッシュテーブルで型が正しく機能するようにします。

0
LIDEN