25

カスタム オブジェクトの 2 つのコレクションを比較するときに、Linq の .Except() メソッドを使用するのに苦労しています。

からクラスを派生させ、 、、および演算子andのObjectオーバーライドを実装しました。メソッドも作成しました。Equals()GetHashCode()==!=CompareTo()

私の 2 つのコレクションでは、デバッグ実験として、各リスト (重複) から最初の項目を取得し、次のように比較しました。

itemListA[0].Equals(itemListB[0]);     // true
itemListA[0] == itemListB[0];          // true
itemListA[0].CompareTo(itemListB[0]);  // 0

3 つのケースすべてで、結果は希望どおりです。ただし、Linq の Except() メソッドを使用すると、重複したアイテムは削除されません。

List<myObject> newList = itemListA.Except(itemListB).ToList();

Linq がどのように比較を行うかを学んで、さまざまな (矛盾する?) メソッドを発見しましIEquatable<T>IEqualityComparer<T>

たとえば、から継承する場合、既にオーバーライドしたものとは異なるシグネチャを持つIEquatable<T>新しいメソッドを提供する必要があるため、混乱しています。Equals()署名の異なる 2 つのメソッドが必要ですか、それとも からクラスを派生させる必要はありませんObjectか?

私のオブジェクト定義(簡略化)は次のようになります。

public class MyObject : Object
{
    public string Name {get; set;}
    public DateTime LastUpdate {get; set;}

    public int CompareTo(MyObject other)
    {
        // ...
    }

    public override bool Equals(object obj)
    {
        // allows some tolerance on LastUpdate
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 17;
            hash = hash * 23 + Name.GetHashCode();
            hash = hash * 23 + LastUpdate.GetHashCode();
            return hash;
        }
    }

    // Overrides for operators
}

orIEquatable<T>を使用して継承できることに気付きました。どちらかを使用すると、署名の要件が変わります。おすすめの方法は?IEquatable<MyObject>IEquatable<object>Equals()

私が達成しようとしていること:

コードを複製することなく、Linq (Distinct/Except) と標準の等価演算子 (==および)を使用できるようにしたいと考えています。この比較では、2 つのオブジェクトの名前が同一で、プロパティが秒数 (ユーザー指定) の許容範囲内にある!=場合に、これらのオブジェクトが等しいと見なされる必要があります。LastUpdate

編集:

GetHashCode()コードを表示しています。

4

3 に答える 3

28

object.Equalsandをオーバーライドするかobject.GetHashCode、実装するIEquatableか、または を提供するかは問題ではありませんIEqualityComparer。それらすべて、わずかに異なる方法で機能します。

1) オーバーライドEqualsおよびGetHashCodefrom object:

これは、ある意味で基本的なケースです。2 つのメソッドの実装が希望どおりであることを確認するために型を編集する立場にあると仮定すると、通常は機能します。多くの場合、これだけを行っても問題はありません。

2) 実装IEquatable

ここで重要な点は、 を実装できる (そして実装すべき) ということですIEquatable<YourTypeHere>。これと #1 の主な違いは、Equalsメソッドにobject. これは、プログラマの利便性 (タイプ セーフの追加) の点で優れているだけでなく、値の型がボックス化されないことを意味するため、カスタム構造体のパフォーマンスを向上させることができます。これを行う場合は、 #1 の代わりではなく、常に#1 に加えて行う必要があります。ここでEqualsメソッドの機能が異なるobject.Equalsと...悪いことになります。そうしないでください。

3) 実装IEqualityComparer

これは最初の 2 つとはまったく異なります。ここでの考え方は、オブジェクトが独自のハッシュ コードを取得していない、またはそれが他の何かと等しいかどうかを確認していないということです。このアプローチのポイントは、オブジェクトが適切にハッシュを取得する方法や、それが何か他のものと等しいかどうかを確認する方法を知らないということです。おそらく、型のコード (つまり、サード パーティのライブラリ) を制御しておらず、動作を上書きしようとしなかったか、上書きしたものの、独自の「等価性」の定義が必要なためです。この特定のコンテキスト。

この場合、2 つの異なるオブジェクトを取り込んで、それらが等しいかどうか、または 1 つのオブジェクトのハッシュ コードが何であるかを通知する、完全に別個の「コンパレータ」オブジェクトを作成します。このソリューションを使用する場合、またはメソッドが型自体で何をするかは問題ではありませんEqualsGetHashCode。使用しません。


これはすべて==、それ自体が獣であるオペレーターとはまったく関係がないことに注意してください。

于 2013-04-03T21:13:27.427 に答える
5

「ある程度の許容範囲を許可」してから、の厳密な値をLastUpdate使用する実装を使用することはできません!GetHashCode()LastUpdate

インスタンスthisLastUpdateat23:13:13.933があり、objインスタンスに があるとします23:13:13.932。次に、これら 2 つは許容範囲のアイデアと同等である可能性があります。ただし、そうであれば、それらのハッシュ コードは同じ数でなければなりません。しかし、非常に幸運でない限り、これは起こりません。なぜなら、DateTime.GetHashCode()はこれら 2 回で同じハッシュを与えるべきではないからです。

その上、あなたのEquals方法は数学的に最も推移的な関係です。また、「ほぼ等しい」を推移的にすることはできません。その推移閉包は、すべてを識別する自明な関係です。

于 2013-04-03T21:12:34.813 に答える