サーチ…


備考

Equals各実装では、次の要件を満たす必要があります。

  • 再帰的 :オブジェクトはそれ自身と同じでなければならない。
    x.Equals(x)true返しtrue

  • 対称 :xとy、またはyとxをxと比較すると、違いはありません。結果は同じです。
    x.Equals(y)y.Equals(x)と同じ値を返します。

  • 推移 :1つのオブジェクトが別のオブジェクトと等しく、このオブジェクトが3番目のオブジェクトに等しい場合、最初のオブジェクトは3番目のオブジェクトに等しくなければなりません。
    if (x.Equals(y) && y.Equals(z))true返したtruex.Equals(z)true返しtrue

  • 一貫性 :オブジェクトを別のものと何度も比較すると、結果は常に同じになります。
    連続するx.Equals(y)呼び出しは、xとyによって参照されるオブジェクトが変更されていない限り、同じ値を返します。

  • nullとの比較 :オブジェクトはnullはなりません。
    x.Equals(null)false返しfalse

GetHashCode実装:

  • Equals互換性があります:2つのオブジェクトが等しい場合( Equalsがtrueを返す場合)、 GetHashCode それぞれのオブジェクトに対して同じ値を返す必要があります。

  • 広い範囲 :2つのオブジェクトが等しくない場合( Equals偽言う)、それらのハッシュコードが明瞭である高い確率が存在すべきです。選択可能な値の数が限られているため、 完璧なハッシングは不可能です。

  • Cheap :すべてのケースでハッシュコードを計算するのは安価でなければなりません。

参照: オーバーロードのためのガイドラインEquals()とオペレータ==

デフォルトは動作と同じです。

EqualsObjectクラス自体で宣言されています。

public virtual bool Equals(Object obj);

デフォルトでは、 Equals動作は次のとおりです。

  • インスタンスが参照型の場合は、参照が同じ場合にのみ、 Equalsはtrueを返します。

  • インスタンスが値型の場合、型と値が同じ場合にのみ、 Equalsはtrueを返します。

  • stringは特殊なケースです。それは値型のように動作します。

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            //areFooClassEqual: False
            Foo fooClass1 = new Foo("42");
            Foo fooClass2 = new Foo("42");
            bool areFooClassEqual = fooClass1.Equals(fooClass2);
            Console.WriteLine("fooClass1 and fooClass2 are equal: {0}", areFooClassEqual);
            //False

            //areFooIntEqual: True
            int fooInt1 = 42;
            int fooInt2 = 42;
            bool areFooIntEqual = fooInt1.Equals(fooInt2);
            Console.WriteLine("fooInt1 and fooInt2 are equal: {0}", areFooIntEqual);

            //areFooStringEqual: True
            string fooString1 = "42";
            string fooString2 = "42";
            bool areFooStringEqual = fooString1.Equals(fooString2);
            Console.WriteLine("fooString1 and fooString2 are equal: {0}", areFooStringEqual);
        }
    }

    public class Foo
    {
        public string Bar { get; }

        public Foo(string bar)
        {
            Bar = bar;
        }
    }
}

良いGetHashCodeオーバーライドを書く

GetHashCodeはDictionary <>およびHashTableに大きなパフォーマンス効果をもたらします。

良いGetHashCodeメソッド

  • 均等な分布を持つべきである
    • すべての整数は、ランダムなインスタンスの返り値とほぼ同じになるはずです
    • メソッドごとにインスタンスごとに同じ整数(例:定数 '999')が返されると、パフォーマンスが低下します
  • 速くなければならない
    • これらは、遅さが特徴である暗号ハッシュではありません
    • ハッシュ関数が遅いほど、辞書の処理速度が遅くなります
  • Equals評価される2つのインスタンスで同じHashCodeを返す必要があります
    • GetHashCodeが乱数を返すなどの理由で、 ListDictionaryなどにアイテムが見つからない場合があります。

GetHashCodeを実装するための良い方法は、開始値として1つの素数を使用し、他の素数で乗算された型のフィールドのハッシュコードをそれに追加することです。

public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 3049; // Start value (prime number).

        // Suitable nullity checks etc, of course :)
        hash = hash * 5039 + field1.GetHashCode();
        hash = hash * 883 + field2.GetHashCode();
        hash = hash * 9719 + field3.GetHashCode();
        return hash;
    }
}

Equalsメソッドで使用されるフィールドのみがハッシュ関数に使用されます。

Dictionary / HashTablesの異なる方法で同じ型を扱う必要がある場合は、IEqualityComparerを使用できます。

カスタム型でのEqualsとGetHashCodeのオーバーライド

Personクラスの場合:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }
}

var person1 = new Person { Name = "Jon", Age = 20, Clothes = "some clothes" };
var person2 = new Person { Name = "Jon", Age = 20, Clothes = "some other clothes" };

bool result = person1.Equals(person2); //false because it's reference Equals

しかし、次のようにEqualsGetHashCodeを定義する:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }

    public override bool Equals(object obj)
    {
        var person = obj as Person;
        if(person == null) return false;
        return Name == person.Name && Age == person.Age; //the clothes are not important when comparing two persons
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode()*Age;
    }
}

var person1 = new Person { Name = "Jon", Age = 20, Clothes = "some clothes" };
var person2 = new Person { Name = "Jon", Age = 20, Clothes = "some other clothes" };

bool result = person1.Equals(person2); // result is true

また、LINQを使用して人に対してさまざまなクエリを実行すると、 EqualsGetHashCode両方がチェックされます。

var persons = new List<Person>
{
     new Person{ Name = "Jon", Age = 20, Clothes = "some clothes"},
     new Person{ Name = "Dave", Age = 20, Clothes = "some other clothes"},
     new Person{ Name = "Jon", Age = 20, Clothes = ""}
};

var distinctPersons = persons.Distinct().ToList();//distinctPersons has Count = 2

IEqualityComparatorのEqualsとGetHashCode

与えられた型Person

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }
}

List<Person> persons = new List<Person>
{
    new Person{ Name = "Jon", Age = 20, Clothes = "some clothes"},
    new Person{ Name = "Dave", Age = 20, Clothes = "some other clothes"},
    new Person{ Name = "Jon", Age = 20, Clothes = ""}
};

var distinctPersons = persons.Distinct().ToList();// distinctPersons has Count = 3

しかし、 EqualsGetHashCodeIEqualityComparator定義する:

public class PersonComparator : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Name == y.Name && x.Age == y.Age; //the clothes are not important when comparing two persons;
    }

    public int GetHashCode(Person obj) { return obj.Name.GetHashCode() * obj.Age; }
}

var distinctPersons = persons.Distinct(new PersonComparator()).ToList();// distinctPersons has Count = 2

このクエリでは、 Equalsがtrueを返し、 GetHashCodeが2人のユーザーに対して同じハッシュコードを返した場合、2つのオブジェクトが等しいとみなされていることに注意してください。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow