C# Language
等しいとGetHashCode
サーチ…
備考
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
返したtrue
、x.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
はObject
クラス自体で宣言されています。
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
が乱数を返すなどの理由で、List
、Dictionary
などにアイテムが見つからない場合があります。
-
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
しかし、次のようにEquals
とGetHashCode
を定義する:
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を使用して人に対してさまざまなクエリを実行すると、 Equals
とGetHashCode
両方がチェックされます。
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
しかし、 Equals
とGetHashCode
をIEqualityComparator
定義する:
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つのオブジェクトが等しいとみなされていることに注意してください。