web-dev-qa-db-ja.com

Dictionary.ContainsKey()-どのように機能しますか?

Dictionary.ContainsKey()の仕組みに関するMSDNドキュメントを読みましたが、実際に等値比較を行う方法を知りたいと思いましたか?基本的に、私は参照タイプ*をキーとするディクショナリを持っており、ContainsKey()メソッドがその参照タイプの特定のプロパティをチェックして、キーが存在するかどうかを判断するための基礎として欲しいと思います。たとえば、Dictionary(MyObject, int)があり、MyObjectに(intの) "TypeID"というパブリックプロパティがある場合、ContainsKey(MyObject myObject)を取得できますか?キーの1つにTypeIDmyObjectと等しいかどうかを確認しますか? ==演算子をオーバーロードできますか?

  • 参照型は、値(double Length)を保持する「Duration」と呼ばれるオブジェクトです。 "Duration"は、私の音楽プログラムで特定のサウンドがどれだけ持続するかを示すために使用される基本タイプです。私はそこから、西洋の音楽の拍子記号などのより洗練されたタイミングの概念を組み込んだクラスを派生させていますが、それらの長さの点ですべて比較できるようにしたいと考えています。

編集:提案されたように、私はオブジェクトにIEquitableを次のように実装しました:

 public class Duration : IEquatable<Duration>
 {
    protected double _length;

    /// <summary>
    /// Gets or Sets the duration in Miliseconds.
    /// </summary>
    public virtual double Length
{
        get
        {
            return _length;
        }
        set
        {
            _length = value;
        }
    }

// removed all the other code that as it was irrelevant

    public override bool Equals(object obj)
    {
        Duration otherDuration = (Duration)obj;
        if (otherDuration._length == _length)
        {
            return true;
        }
        else
        {
            return false
        }
    }

}

これで十分ですか?

21
Richard

編集:ここに更新された例のコードがあります。注:フィールドを保護されているものとして公開し、メンバーを公開する仮想プロパティもあるのは少し変だと思います。このスキームでは、何かがLengthをオーバーライドして、期待どおりに動作しないように__lenght_を等価にする可能性があります。

_public class Duration : IEquatable<Duration>
{
    protected double _length;

    /// <summary>
    /// Gets or Sets the duration in Miliseconds.
    /// </summary>
    public virtual double Length
    {
        get { return _length; }
        set { _length = value; }
    }

    // removed all the other code that as it was irrelevant

    public bool Equals(Duration other)
    {
        // First two lines are just optimizations
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;

        return _length.Equals(other._length);
    }

    public override bool Equals(object obj)
    {
        // Again just optimization
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;

        // Actually check the type, should not throw exception from Equals override
        if (obj.GetType() != this.GetType()) return false;

        // Call the implementation from IEquatable
        return Equals((Duration) obj);
    }

    public override int GetHashCode()
    {
        // Constant because equals tests mutable member.
        // This will give poor hash performance, but will prevent bugs.
        return 0;
    }
}
_

辞書クラスで使用されるデフォルトの IEqualityComparer については、 EqualityComparer.Default を参照してください。

クラスでGetHashCodeEqualsを一般的にオーバーライドしたくない場合、またはそれができない場合。 Dictionaryコンストラクターのオーバーロード があり、使用する特定の IEqualityComparer を指定できます。

これは実装するシンプルなインターフェースですが、GetHashCodeの規約を尊重するように注意する必要があります。そうしないと、予期しない動作が発生する可能性があります。

_public class MyObjectEqualityComparer : IEqualityComparer<MyObject>
{
    public bool Equals(MyObject x, MyObject y)
    {
        return x.TypeID == y.TypeID;
    }

    public int GetHashCode(MyObject obj)
    {
        return obj.TypeID; //Already an int
    }
}
_

使うだけ

_new Dictionary<MyObject, int>(new MyObjectEqualityComparer());   
_

デフォルトのIEqualityComparerを使用する場合は、MyObjectEqualityComparerでほぼ同じメソッドを提供する必要があります。あなたはcanIEquatable を実装する場合、object.Equals()のオーバーライドを回避できます=。ただし、これを行うと意外な動作が発生する可能性があるため、お勧めしません。 Equalsをオーバーライドすることをお勧めします。これにより、Equalsへのすべての呼び出しに対して一貫した動作が行われ、Equalsに適切に一致するハッシュが得られます。過去の開発者が_IEquatable._のみを実装したために発生した継承コードのバグを修正する必要がありました

11
vossad01

内部的にはDictionaryEqualityComparerを使用します。まず、キーが IEquatable を実装しているかどうかをチェックします。 keyがこのインターフェースを実装していない場合は、Equalsメソッドを呼び出します。

7