web-dev-qa-db-ja.com

IEqualityComparerとEquals / GethashCode Overrideの使用の違いは何ですか?

辞書を使用しているとき、キーを比較するためにデフォルトのEqualsの意味を変更しなければならないことがあります。キーのクラスでEqualsとGetHashCodeをオーバーライドするか、IEqualityComparerを実装する新しいクラスを作成すると、同じ結果が得られることがわかります。では、IEqualityComparerとEquals/GethashCodeOverrideを使用することの違いは何ですか? 2つの例:

class Customer
{
    public string name;
    public int age;
    public Customer(string n, int a)
    {
        this.age = a;
        this.name = n;
    }
    public override bool Equals(object obj)
    {
        Customer c = (Customer)obj;
        return this.name == c.name && this.age == c.age;
    }
    public override int GetHashCode()
    {
        return (this.name + ";" + this.age).GetHashCode();
    }
}
  class Program
{
    static void Main(string[] args)
    {
        Customer c1 = new Customer("MArk", 21);
        Customer c2 = new Customer("MArk", 21);
        Dictionary<Customer, string> d = new Dictionary<Customer, string>();
        Console.WriteLine(c1.Equals(c2));
        try
        {
            d.Add(c1, "Joe");
            d.Add(c2, "hil");
            foreach (KeyValuePair<Customer, string> k in d)
            {
                Console.WriteLine(k.Key.name + " ; " + k.Value);
            }
        }
        catch (ArgumentException)
        {
            Console.WriteLine("Chiave già inserita in precedenza");
        }
        finally
        {
            Console.ReadLine();
        }
    }
}

}

二つ目 :

class Customer
{
    public string name;
    public int age;
    public Customer(string n, int a)
    {
        this.age = a;
        this.name = n;
    }
}
class DicEqualityComparer : EqualityComparer<Customer>
{
    public override bool Equals(Customer x, Customer y) // equals Dell'equalitycomparer
    {
        return x.name == y.name && x.age == y.age;
    }
    public override int GetHashCode(Customer obj)
    {
        return (obj.name + ";" + obj.age).GetHashCode();
    }
}
class Program
{
    static void Main(string[] args)
    {
        Customer c1 = new Customer("MArk", 21);
        Customer c2 = new Customer("MArk", 21);
        DicEqualityComparer dic = new DicEqualityComparer();
        Dictionary<Customer, string> d = new Dictionary<Customer, string>(dic);
        Console.WriteLine(c1.Equals(c2));
        try
        {
            d.Add(c1, "Joe");
            d.Add(c2, "hil");
            foreach (KeyValuePair<Customer, string> k in d)
            {
                Console.WriteLine(k.Key.name + " ; " + k.Value);
            }
        }
        catch (ArgumentException)
        {
            Console.WriteLine("Chiave già inserita in precedenza");
        }
        finally
        {
            Console.ReadLine();
        }
    }
}

}

どちらの例でも同じ結果になります。

前もって感謝します。

21
dariush624

EqualsGetHashCodeをオーバーライドすると、オブジェクトが別のオブジェクトと等しいかどうかを判断する方法が変わります。また、==演算子を使用してオブジェクトを比較する場合、演算子もオーバーライドしない限り、Equalsと同じ動作にはなりません。

単一のクラスの動作を変更した場合、他のクラスにも同じロジックが必要な場合はどうなりますか? 「一般的な比較」が必要な場合。それがあなたがIEqualityComparerを持っている理由です。

この例を見てください:

interface ICustom
{
    int Key { get; set; }
}
class Custom : ICustom
{
    public int Key { get; set; }
    public int Value { get; set; }
}
class Another : ICustom
{
    public int Key { get; set; }
}

class DicEqualityComparer : IEqualityComparer<ICustom>
{
    public bool Equals(ICustom x, ICustom y)
    {
        return x.Key == y.Key;
    }

    public int GetHashCode(ICustom obj)
    {
        return obj.Key;
    }
}

私には2つの異なるクラスがあり、どちらも同じ比較器を使用できます。

var a = new Custom { Key = 1, Value = 2 };
var b = new Custom { Key = 1, Value = 2 };
var c = new Custom { Key = 2, Value = 2 };
var another = new Another { Key = 2 };

var d = new Dictionary<ICustom, string>(new DicEqualityComparer());

d.Add(a, "X");
// d.Add(b, "X"); // same key exception
d.Add(c, "X");
// d.Add(another, "X"); // same key exception

どちらのクラスでもEqualsGetHashCodeをオーバーライドする必要がないことに注意してください。この比較子は、比較ロジックを書き直すことなく、ICustomを実装する任意のオブジェクトで使用できます。 「親クラス」用にIEqualityComparerを作成し、継承するクラスで使用することもできます。別の方法で動作する比較子を作成できます。Valueの代わりにKeyを比較する比較子を作成できます。

したがって、IEqualityComparerを使用すると柔軟性が高まり、一般的なソリューションを実装できます。

14
BrunoLM

オブジェクトのEquals() anf GetHashCode()は、オブジェクトに固有の平等の概念を実装します。ただし、同等性の代替概念を使用することもできます。たとえば、完全な住所ではなく郵便番号のみを使用する住所オブジェクトの同等性比較ツールです。

4

100%の同等性以外のものを使用して、Dictionaryでオブジェクトを検索したい場合が多くあります。簡単な例として、大文字と小文字を区別しない方法で一致する辞書が必要な場合があります。これを実現する1つの方法は、文字列を辞書に保存したりルックアップを実行したりする前に、文字列を正規の大文字形式に変換することです。別のアプローチは、辞書にIEqualityComparer<string>を提供することです。これは、ハッシュコードを計算し、ある種のケースに依存しない関数で等しいかどうかをチェックします。文字列を正規の形式に変換し、可能な限りその形式を使用する方が効率的な場合もありますが、文字列を元の形式でのみ保存する方が効率的な場合もあります。このような辞書の有用性を向上させる.NETの機能の1つは、特定のキーに関連付けられた実際のキーオブジェクトを要求する手段です(したがって、辞書に文字列"WowZo"がキーとして含まれている場合、次のようになります。 up "wowzo" and get "WowZo";残念ながら、TValueに冗長な参照が含まれていない場合、実際のキーオブジェクトを取得する唯一の方法は、コレクション全体を列挙することです) 。

別の比較手段があると便利な別のシナリオは、オブジェクトがインスタンスへの参照を変更可能な型で保持しているが、そのインスタンスを変更する可能性のあるものに決して公開しない場合です。一般に、同じ値のシーケンスを保持するint[]の2つのインスタンスは互換性がありません。これは、将来、それらの一方または両方が異なる値を保持するように変更される可能性があるためです。一方、辞書を使用してint[]値を保持および検索する場合、各値はint[]のインスタンスへのユニバース内の唯一の参照であり、いずれもインスタンスは変更されるか、外部コードに公開されます。同じ値のシーケンスを保持する等しい配列インスタンスと見なすと便利な場合があります。 Array.Equalsは厳密な同等性(参照の同等性)をテストするため、配列の同等性をテストする他の手段を使用する必要があります。

1
supercat

この目的でも基本的に同じですが、わずかな違いが1つあります。最初の例では、Object型のパラメーターを使用してEqualsをオーバーライドし、それをCustomerにキャストする必要がありますが、2番目の例では、Customer型のパラメーターを持つことができるため、キャストする必要はありません。

つまり、Equalsをオーバーライドすると、異なるタイプの2つのオブジェクト(特定の状況で必要になる場合があります)を比較できますが、IEqualityComparerを実装しても、この自由は得られません(特定の状況でも必要になる場合があります)。

1
Kyle