オブジェクトのペアが等しいかどうかを確認するには、参照の等価性と構造/値の等価性の 2 つの方法があります。参照の等価性はすべての参照型 (クラス) のデフォルトであり、構造の等価性はすべての値型のデフォルトです (ただし、デフォルトの実装は最適ではありません)。次のガイドを使用して、参照型と値型の両方に構造的等価性を実装します。
平等
等価チェックは、次の規則に従う必要があります。
- オブジェクトはそれ自体と等しい (同一性)
- との比較は、との比較と同じ真実
x
を返します。(対称)y
y
x
x
が に等しくy
、が に等しい場合、y
は に等しくなければなりません。(推移性)z
x
z
- オブジェクトが と等しくなることはありません
null
。
null
に等しいnull
です。
- 例外はスローされません。
クラスまたは構造体IEquatable<T>
にカスタム等値チェック用のインターフェイスを実装させてから、 メソッドIEquatable<T>.Equals(T)
とObject.Equals()
メソッドを実装します。
参照型 (クラス) の等価性
IEquatable<T>.Equals(T)
参照型の場合、次のようにメソッドを実装します。
public bool Equals(MyType other)
{
if (Object.ReferenceEquals(other, null) || // When 'other' is null
other.GetType() != this.GetType()) // or of a different type
return false; // they are not equal.
return this.field1 == other.field1
&& this.field2 == other.field2;
}
Object.Equals()
次に、次のようにオーバーライドします。
public override bool Equals(object obj)
{
return Equals(obj as MyType);
}
値型 (構造体) の等価性
値の型を にすることはできないため、次のようにメソッドnull
を実装します。IEquatable<T>.Equals(T)
public bool Equals(MyType other)
{
return this.field == other.field
&& this.field2 == other.field2;
}
Object.Equals()
次に、次のようにオーバーライドします。
public override bool Equals(object obj)
{
if (!(obj is MyType))
return false;
return Equals((MyType)obj);
}
等値演算子
参照型と値型の両方について、デフォルトの等値演算子と不等値演算子をオーバーライドしたい場合があります。Jon Skeet によるこの投稿に基づいて、等値演算子と不等値演算子は次のように実装できます。
public static bool operator ==(MyType left, MyType right)
{
return Object.Equals(left, right);
}
public static bool operator !=(MyType left, MyType right)
{
return !(left == right);
}
left
and/or right
isnull
の場合は、オーバーライドObject.Equals(object, object)
を呼び出さない(したがって、メソッドを呼び出さない)ことに注意してください。Object.Equals(object)
IEquatable<T>.Equals(T)
ハッシュコード
オブジェクトがディクショナリまたはハッシュ テーブルに配置される場合など、オブジェクトのハッシュ コードが重要な場合があります。一般に、Equals()
メソッドをオーバーライドするときは、メソッドをオーバーライドしますGetHashCode()
。ハッシュ コードは次の規則に従う必要があります。
- オブジェクト内のいくつかのフィールドを変更した後でも、ハッシュ コードは決して変更しないでください。
- 等しいと見なされるオブジェクトの場合、ハッシュ コードは等しい必要があります。
- ハッシュ コードは、等しくないと見なされるオブジェクトの任意のもの (等しいものを含む) である可能性があります。
- ハッシュ コードはランダムに配布する必要があります。
- ハッシュ コード関数は例外をスローしてはならず、常に返さなければなりません。
- ハッシュ コードは非常に高速に計算する必要があります。
したがって、Object.GetHashCode()
構造的等価性を使用するクラスまたは構造体を実装するには、オブジェクトから不変のフィールドをいくつか選択し、それらをマークしますreadonly
。これらのフィールドのみを使用して、ハッシュ コードを計算します。Object.GetHashCode()
メソッドをオーバーライドして、次のように実装します。
public override int GetHashCode()
{
unchecked
{
int hash = 17;
// Don't forget to check for null values.
hash = hash * 29 + field1.GetHashCode();
hash = hash * 29 + field2.GetHashCode();
// ...
return hash;
}
}
または、不変フィールドが 1 つしかない場合は、次のものを使用することを検討できます。
public override int GetHashCode()
{
// Don't forget to check for null values.
return field1.GetHashCode();
}
不変フィールドがない場合は、定数ハッシュ コードを返します。たとえば、型自体のハッシュ コードです。
public override int GetHashCode()
{
return GetType().GetHashCode();
}
コレクションの構造的等価性
Equals()
デフォルトの方法を使用してコレクションを比較しないでください。代わりに、コレクションのデフォルトの等価性は参照等価性にする必要があります。構造的等価性も実装するには、IStructuralEquatable
インターフェイスを実装します。例えば:
bool IStructuralEquatable.Equals(object obj, IEqualityComparer comparer)
{
var other = obj as MyType;
if (other == null)
return false;
return ((IStructuralEquatable)this.innerArray)
.Equals(other.innerArray, comparer);
}
int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
{
return ((IStructuralEquatable)this.innerArray).GetHashCode(comparer);
}