IEquatable と GetHashCode() はセットで実装
概要
.NET (C#, VB.NET) で、ジェネリックコレクションに独自の型を格納するときは、 IEquatable
GetHashCode() の実装は忘れやすいため、注意が必要です。これらを実装することで、独自の型でキーが同じかどうかを確認できるようになります。
背景
MSDN で Dictionary
Dictionary は、キーが同じであるかどうかを確認するための等値比較の実装を必要とします。comparer パラメータを受け付けるコンストラクタを使用して、IEqualityComparer ジェネリック インターフェイスの実装を指定できます。実装を指定しない場合は、既定のジェネリック等値比較演算子である EqualityComparer.Default が使用されます。型 TKey が System.IEquatable ジェネリック インターフェイスを実装している場合は、既定の等値比較演算子でその実装が使用されます。
この記述だけを見ると、作成したクラスに IEquatable
試しに GetHashCode() の実装を行わず IEquatable
Dictionary
サンプル
IEquatable
public class Person : IEquatable<Person> { private string name; private DateTime birthday; public string Name { get { return name; } } public DateTime Birthday { get { return birthday; } } public Person(string name, DateTime birthday) { this.name = name; this.birthday = birthday; } // 実装するのを忘れやすいので注意 public override int GetHashCode() { return name.GetHashCode() ^ birthday.GetHashCode(); } #region IEquatable<Person> メンバ bool IEquatable<Person>.Equals(Person other) { if (other == null) { return false; } return name == other.name && birthday == other.birthday; } #endregion }
Dictionary
実行すると「address: Osaka」が返され、比較が正しく行われていることがわかります。
Dictionary<Person, string> addressBook = new Dictionary<Person, string>(); addressBook.Add(new Person("foo", new DateTime(2007, 3, 4)), "Tokyo"); addressBook.Add(new Person("bar", new DateTime(2007, 4, 1)), "Osaka"); addressBook.Add(new Person("bar", new DateTime(2007, 4, 2)), "Hokkaido"); string address; if (addressBook.TryGetValue(new Person("bar", new DateTime(2007, 4, 1)), out address)) { System.Diagnostics.Debug.WriteLine("address: " + address); } else { System.Diagnostics.Debug.WriteLine("address: not found"); } // [実行結果] // address: Osaka