0

私はかなり堅牢であると信じている GetHashCode の実装を持っていますが、正直なところ、私はそれをインターネットの奥深くから掘り起こしました。 ' または GetHashCode の「不適切な」実装です。

私は GetHashCode について StackOverflow で多くのことを読みました。NHibernate で Equals/GetHashCode を上書きする必要がある理由のサンプルはありますか? このスレッドはおそらく最良の情報源だと思いますが、それでも疑問に思いました。

次のエンティティと、Equals および GetHashCode の特定の実装について考えてみましょう。

public class Playlist : IAbstractDomainEntity
{
    public Guid Id { get; set; }
    public string Title { get; set; 
    public Stream Stream { get; set; }
    //  Use interfaces so NHibernate can inject with its own collection implementation.
    public IList<PlaylistItem> Items { get; set; }
    public PlaylistItem FirstItem { get; set; }
    public Playlist NextPlaylist { get; set; }
    public Playlist PreviousPlaylist { get; set; }

    private int? _oldHashCode;
    public override int GetHashCode()
    {
        // Once we have a hash code we'll never change it
        if (_oldHashCode.HasValue)
            return _oldHashCode.Value;

        bool thisIsTransient = Equals(Id, Guid.Empty);

        // When this instance is transient, we use the base GetHashCode()
        // and remember it, so an instance can NEVER change its hash code.
        if (thisIsTransient)
        {
            _oldHashCode = base.GetHashCode();
            return _oldHashCode.Value;
        }
        return Id.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        Playlist other = obj as Playlist;
        if (other == null)
            return false;

        // handle the case of comparing two NEW objects
        bool otherIsTransient = Equals(other.Id, Guid.Empty);
        bool thisIsTransient = Equals(Id, Guid.Empty);
        if (otherIsTransient && thisIsTransient)
            return ReferenceEquals(other, this);

        return other.Id.Equals(Id);
    }
}

この実装で宣伝されている安全性チェックの量は、過剰に思えます。これを書いた人が私よりも多くの特殊なケースを理解していたと仮定すると、それは私に自信を与えますが、なぜこれほど多くの単純化された実装を見ているのか疑問に思います。

Equals メソッドがオーバーライドされたときに GetHashCode をオーバーライドすることが重要なのはなぜですか? これらのさまざまな実装をすべて見てください。以下は、シンプルでありながら高い評価を得ている実装です。

  public override int GetHashCode()
  {
        return string.Format("{0}_{1}_{2}", prop1, prop2, prop3).GetHashCode();
  }

この実装は、私が提供したものよりも優れていますか、それとも劣っていますか? なんで?

どちらも等しく有効ですか?GetHashCode を実装するときに従うべき標準の「ガイドライン」はありますか? 上記の実装に明らかな欠陥はありますか? GetHashCode の実装を検証するテスト ケースを作成するにはどうすればよいですか?

4

2 に答える 2

1

X残念なことに、等値テスト メソッドがオブジェクト参照との任意のペアに対して意味のある質問をする可能性がある 2 つの異なる質問がありますが、1 つのメソッドと 1 つYのメソッドしかありません。EqualsGetHashCode

  • XYが同じ型 (*) であると仮定すると、 のすべてのメンバーはX常に の対応するメソッドと同じように動作しYますか? 異なる配列への 2 つの参照は、一致する要素が含まれていても、この定義では等しくないと報告されます。これは、それらの要素が一時的に同じであっても、常にそうであるとは限らないためです。

  • XYが同じ型 (*) であると仮定すると、 object へのすべての参照を objectXへの参照に同時に置き換えたり、その逆にしたりすると、ID ベースの関数Y以外のメンバーに影響しますか? GetHashCode要素が一致する 2 つの異なる配列への参照は、この定義では等しいと報告されます。

(*) 一般に、異なるタイプのオブジェクトは等しくないことを報告する必要があります。プライベート クラスにアクセスできるすべてのコードが一致するパブリック タイプに参照を格納するだけである場合、同じパブリック クラスから継承された異なるプライベート クラスのオブジェクトは等しいと見なされるべきであると主張できる場合があるかもしれませんが、それはせいぜいかなり狭い例外です。

最初の質問をする必要がある状況もあれば、2 番目の質問をする必要がある状況もあります。デフォルトObjectの実装は最初に答え、デフォルトのEquals実装は2番目に答えます。残念ながら、特定の参照に適した比較方法の選択は、参照先のインスタンスの型の関数ではなく、参照の使用方法の関数です。2 つのオブジェクトがコレクションへの参照を保持している場合、その内容をカプセル化する目的で、変更したり、変更したりする可能性のあるコードに公開したりしない場合、それらの参照を保持するオブジェクトの等価性は、コレクションの ID ではなく、コレクションの内容に依存する必要があります。 .GetHashCodeValueType

PlayListコードは、最初の質問がより適切な方法で型のインスタンスを使用している場合があり、2 番目の質問がより適切な方法で使用しているように見えます。それは実行可能かもしれませんが、必要に応じて、等値チェックメソッドがいずれかの用途に適したオブジェクトによってラップできる共通のデータホルダーオブジェクトを用意する方がよいと思います (たとえばPlaylistDataMutablePlaylistまたは)でラップされますImmutablePlaylist。ラッパー クラスは、ラッパーを無効にし、オブジェクトの新しいラッパーを返すメソッドを持つことができます (ラッパーを使用すると、特定の参照がそれを変更する可能性のあるコードに公開される可能性があるかどうかをシステムが確実に知るInvalidateAndMakeImmutableことができます)。InvalidateAndMakeMutablePlaylist

于 2013-07-08T22:07:00.483 に答える