2

いくつかの複雑なオブジェクトを作成しましたが、単体テストを行って正しく動作することを確認しようとしています。これにはいくつかの List(Of T) の比較が含まれるため、CollectionAssert を使用しようとしました。今、私は奇妙なことに遭遇しました。

最初に CollectionAssert.AreEqual を使用して、最初のリストが等しいかどうかを確認しました。この主張は通りました。しかし、簡単にするために、CollectionAssert.AreEqual を使用して、期待されるオブジェクトを正確な順序で作成する必要がないようにしたかったので、それを試し始めました。まったく同じコードでは、CollectionAssert.AreEquivalent が失敗しました。equal は equal よりも緩やかなアサーションであるため、これは奇妙だと思いますよね?次のエラーが表示されます。

CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s) of <MyObject>. The actual collection contains 0 occurrence(s).

デバッグを試みましたが、シンボル ファイルのダウンロードを設定したにもかかわらず、.Net フレームワークのデバッグがうまくいきませんでした。そのため、カスタムの Equals 関数に 1 回しか入力されず (true を返す)、アサーションが失敗することがわかりました。どちらのオブジェクトにも 2 つの要素があります。コール スタックは次のとおりです (逆順)。

  • コレクションAssert.AreEquivalent
  • CollectionAssert.AreEquivalent(オーバーロード)
  • CollectionAssert.FindMisMatchedElement
  • Generic.Dictionary(Of Object, int).TryGetValue
  • Generic.Dictionary(Of Object, int).FindEntry
  • Generic.ObjectEqualityComparer.Equals
  • 私のカスタム Equals

これを書いている今、アイデアが思い浮かび、潜在的な問題があることがわかりました。内部的に Dictionary を使用していることがわかります。int が実際のリストのインデックスである場合、これはおそらくある種のハッシュマップとして機能しますか? これは、equals をオーバーライドするのではなく、カスタム IEqualityComparer を実装する必要があるということですか? そして、私の getHashCode() はどのように見えるでしょうか? (辞書のキーに使用される可能性があると思うので、これは非常に重要だと思いますか?)

4

1 に答える 1

2

あなたは正しい道を進んでいます。実際、問題は、オーバーライドGetHashCodeしている間にオーバーライドしないことですEquals

問題を再現する例を次に示します。

void Main()
{
    var a = new []{new Broken{Foo="a"}, new Broken{Foo="b"}};
    var b = new []{new Broken{Foo="a"}, new Broken{Foo="b"}};

    CollectionAssert.AreEqual(a, b);
    CollectionAssert.AreEquivalent(a, b);
}

class Broken
{
    public string Foo {get;set;}

    public override bool Equals(object obj)
    {
        return Foo == ((Broken)obj).Foo;
    }
}

あなたが正しく指摘したように、CollectionAssert.AreEquivalentを使用Dictionaryし、コレクション内の各一意の要素の頻度をカウントするために使用されます。

問題は、ハッシュコードが衝突する可能性があることではありませんが、等しいと見なされるべき 2 つの要素が実際にはEqualsif によって返されるハッシュコードGetHashCodeが異なる場合に比較されることはありません。


この質問にも興味があるかもしれません:

Equals メソッドがオーバーライドされたときに GetHashCode をオーバーライドすることが重要なのはなぜですか?


また、equals 関数には、すべての文字列を 1 対 1 で直接比較しないロジックがいくつかあります。つまり、等しいオブジェクトが同じハッシュコードを取得することを保証するために、基本的にこの機能を 2 回実装することになります。

必ずしも。のパフォーマンスはDictionary、ハッシュ アルゴリズムに依存します (また、オブジェクトがキーとして使用されている間は、オブジェクトのハッシュ値も変化してはなりません)。

いくつかのパフォーマンス ペナルティ (おそらく無視できる程度) を受け入れることができる場合は、メソッドで使用するよりも単純な方法を使用してハッシュ値を計算できますEquals(そして、いくつかのより多くのハッシュ衝突を受け入れることができます)。2 つのオブジェクトのハッシュ値が等しい場合Equalsは、とにかく呼び出されます。(実際には、毎回同じ値、たとえば 1 を返すだけで済む場合があります)。


ドキュメントの関連セクション:

Object.GetHashCode

Hashtable オブジェクトでキーとして使用されるオブジェクトも、GetHashCode メソッドをオーバーライドする必要があります。これらのオブジェクトは、独自のハッシュ コードを生成する必要があるためです。

2 つのオブジェクトを比較して等しい場合、各オブジェクトの GetHashCode メソッドは同じ値を返す必要があります。ただし、2 つのオブジェクトを比較して等しくない場合、2 つのオブジェクトの GetHashCode メソッドは異なる値を返す必要はありません。

于 2013-04-06T10:33:13.153 に答える