オブジェクトの同一性を見分ける EqualityComparer の履歴(No.1)
更新object.Equals と object.GetHashCode†
これらをオーバーライドすることで、同一の内容を持つ異なるオブジェクトを 同一視することができる。
LANG:C# using System.Collections.Generic; class TestClass { public string text; public override bool Equals(object obj) { return ( obj is TestClass ) && ( (TestClass)obj ).text == text; } public override int GetHashCode() { return text.GetHashCode() * 13; } public TestClass(string text) { this.text = text; } } ... var a = new TestClass("a"); var b = new TestClass("a"); var list = new List<TestClass>(); Assert.IsFalse( a == b ); Assert.AreEqual(a, b); list.Add(a); Assert.IsTrue(list.Contains(b)); // 同じ内容の異なるインスタンス
今度は見分けられなくなる†
で、これはこれで良いのだけれど、今度は異なるインスタンスを見分けたい時に困る。
解決するには EqualityComparer というのを実装すれば良いと書いてあるのだが、
.Equals( a, b ) は、return a == b; とか、return object.ReferenceEquals(a, b); などとすれば良いとして、
.GetHashCode( a ) をどうするか。object.GetHashCode を呼び出したくても オーバーライドしてしまったものは正攻法で呼び出すことができない。
そこで、リフレクションの System.Reflection.MethodInfo.GetBaseDefinition() を使って以下のように解決した。
LANG:C# using System.Collections.Generic; /// <summary> /// Equality comparer that uses Object.ReferenceEquals(x, y) to compare class values. /// </summary> /// <typeparam name="T"></typeparam> public class EqualityComparerByRef<T>: EqualityComparer<T> where T: class { /// <summary> /// Determines whether two objects of type T are equal by calling Object.ReferenceEquals(x, y). /// </summary> /// <param name="x">The first object to compare.</param> /// <param name="y">The second object to compare.</param> /// <returns>true if the specified objects are equal; otherwise, false.</returns> public override bool Equals(T x, T y) { return Object.ReferenceEquals(x, y); } System.Reflection.MethodInfo getHashCode; /// <summary> /// Initializes a new instance of the EqualityComparerByRef<T> class. /// </summary> public EqualityComparerByRef() { var getHashCode = typeof(T).GetMethod("GetHashCode", Type.EmptyTypes); while ( getHashCode.DeclaringType != typeof(object) ) getHashCode = getHashCode.GetBaseDefinition(); } /// <summary> /// Serves as a hash function for the specified object for hashing algorithms and /// data structures, such as a hash table. /// </summary> /// <param name="obj">The object for which to get a hash code.</param> /// <returns>A hash code for the specified object.</returns> /// <exception cref="System.ArgumentNullException"><paramref name="obj"/> is null.</exception> public override int GetHashCode(T obj) { return (int)getHashCode.Invoke(obj, new object[0]); } /// <summary> /// Returns a default equality comparer for the type specified by the generic argument. /// </summary> /// <value>The default instance of the System.Collections.Generic.EqualityComparer<T> /// class for type T.</value> public static EqualityComparerByRef<T> Default { get { return _default; } } static EqualityComparerByRef<T> _default = new EqualityComparerByRef<T>(); } ... var a = new TestClass("a"); var b = new TestClass("a");
var DefaultComparer = EqualityComparer<TestClass>.Default; var ComparerByRef = EqualityComparerByRef<TestClass>.Default; Assert.IsTrue(DefaultComparer.Equals(a, a)); // same object Assert.IsTrue(ComparerByRef.Equals(a, a)); // same object
Assert.IsTrue(DefaultComparer.Equals(a, b)); // different object with same value Assert.IsFalse(ComparerByRef.Equals(a, b)); // different object with same value var list = new List<TestClass>();
list.Add(a); Assert.IsTrue(list.Contains(b)); Assert.IsFalse(list.Contains(b, ComparerByRef));
コメント†
Counter: 13865 (from 2010/06/03),
today: 2,
yesterday: 4