web-dev-qa-db-ja.com

IEquatable <T>を使用するタイミングと理由

IEquatable<T> 購入しますか?それが有用であると私が見ることができる唯一の理由は、ジェネリック型を作成し、ユーザーに適切なequalsメソッドを実装および作成するように強制するときです。

何が欠けていますか?

44
Jack Kada

[〜#〜] msdn [〜#〜] から:

IEquatable(T)インターフェイスは、Dictionary(TKey, TValue)List(T)LinkedList(T)などの一般的なコレクションオブジェクトによって使用されますContainsIndexOfLastIndexOfRemoveなどのメソッドで等価性をテストする場合。

IEquatable<T>実装では、これらのクラスに必要なキャストが1つ少ないため、標準より少し高速ですobject.Equalsそれ以外の場合に使用されるメソッド。例として、2つのメソッドの異なる実装を見てください。

public bool Equals(T other) 
{
  if (other == null) 
     return false;

  return (this.Id == other.Id);
}

public override bool Equals(Object obj)
{
  if (obj == null) 
     return false;

  T tObj = obj as T;  // The extra cast
  if (tObj == null)
     return false;
  else   
     return this.Id == tObj.Id;
}
41
Cornelius

ここで最も重要な理由が言及されていないことに驚いています。

_IEquatable<>_は主に2つの理由で構造体のために導入されました:

  1. 値型(構造体の読み取り)の場合、ジェネリックでないEquals(object)にはボックス化が必要です。 _IEquatable<>_を使用すると、構造体に厳密に型指定されたEqualsメソッドを実装できるため、ボクシングは必要ありません。

  2. 構造体の場合、Object.Equals(Object)のデフォルト実装(これは_System.ValueType_でオーバーライドされたバージョンです)は、リフレクションを使用して型内のすべてのフィールドの値を比較することにより、値の等価性チェックを実行します。実装者が構造体の仮想Equalsメソッドをオーバーライドする場合、目的は、値の等価性チェックを実行するより効率的な手段を提供し、オプションで構造体のフィールドまたはプロパティのサブセットに基づいて比較を行うことです。

どちらもパフォーマンスを向上させます。

参照型(読み取りクラス)はそれほどメリットがありません。 _IEquatable<>_の実装では、_System.Object_からのキャストを回避できますが、これは非常に小さな利益です。 _IEquatable<>_は論理的にインテントを明示的にするので、クラスに実装するのが好きです。

37
nawfal

私はIEquatable<T>をかなり使用していますが、純粋な技術的な観点からは、特定の利点は実際にはありません。 System.Object.Equalsをオーバーライドすると、同じ機能を提供できます。

ただし、IEquatable<T>の実装のexplicitnessが好きです。 EntitiesおよびValue Objectsの概念を ドメイン駆動設計 かなり多く、特に値オブジェクトにはIEquatable<T>を使用します。これは、型が明確に定義された等価性を持っていることを示すためです。

10
Mark Seemann

他の答えに加えて、ここでは値型に対して_IEquatable<T>_を実装する(そしてEquals(object)も明らかにオーバーライドする)非常に良い理由があります。それ以外の場合に呼び出されるデフォルトのValueType.Equals(object)コードを見てください。ボクシング、タイプ評価を導入し、フィールドのいずれかが参照タイプの場合、最終的にリフレクションにフォールバックするのは絶対的なパフォーマンスキラーです。

_public override bool Equals(object obj)
{
    if (obj == null)
    {
        return false;
    }
    RuntimeType type = (RuntimeType) base.GetType();
    RuntimeType type2 = (RuntimeType) obj.GetType();
    if (type2 != type)
    {
        return false;
    }
    object a = this;
    if (CanCompareBits(this))
    {
        return FastEqualsCheck(a, obj);
    }
    FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    for (int i = 0; i < fields.Length; i++)
    {
        object obj3 = ((RtFieldInfo) fields[i]).UnsafeGetValue(a);
        object obj4 = ((RtFieldInfo) fields[i]).UnsafeGetValue(obj);
        if (obj3 == null)
        {
            if (obj4 != null)
            {
                return false;
            }
        }
        else if (!obj3.Equals(obj4))
        {
            return false;
        }
    }
    return true;
}
_

特定のシナリオ(辞書でキーとして値タイプを使用するなど)では、1回の不正な操作でパフォーマンスを殺す可能性があります。

2
0b101010