1

MSDN が必要だと言っているすべての機能に加えて、いくつかの追加の比較インターフェイスを実装しましたが、何も機能していないようです。以下はコードです (LinqPad 用に最適化されています)。結果の出力は、私が期待する 2 項目ではなく、すべて 4 項目です。回避策を回答として投稿しないでください - Distinct の仕組みを知りたいです

void Main()
{
  List<NameClass> results = new List<NameClass>();
  results.Add(new NameClass("hello"));
  results.Add(new NameClass("hello"));
  results.Add(new NameClass("55"));
  results.Add(new NameClass("55"));
  results.Distinct().Dump();
}

// Define other methods and classes here

  public class NameClass : Object
    , IEquatable<NameClass>
    , IComparer<NameClass>
    , IComparable<NameClass>
    , IEqualityComparer<NameClass>
    , IEqualityComparer
    , IComparable
  {

    public NameClass(string name)
    {
      Name = name;
    }

    public string Name { get; private set; }

    public int Compare(NameClass x, NameClass y)
    {
      return String.Compare(x.Name, y.Name);
    }

    public int CompareTo(NameClass other)
    {
      return String.Compare(Name, other.Name);
    }

    public bool Equals(NameClass x, NameClass y)
    {
      return (0 == Compare(x, y));
    }

    public bool Equals(NameClass other)
    {
      return (0 == CompareTo(other));
    }

    public int GetHashCode(NameClass obj)
    {
      return obj.Name.GetHashCode();
    }

    public new int GetHashCode()
    {
      return Name.GetHashCode();
    }

    public new bool Equals(object a)
    {
      var x = a as NameClass;
      if (null == x) { return false; }
      return Equals(x);
    }

    public new bool Equals(object a, object b)
    {
      if (null == a && null == b) { return true;  }
      if (null == a && null != b) { return false; }
      if (null != a && null == b) { return false; }
      var x = a as NameClass;
      var y = b as NameClass; 
      if (null == x && null == y) { return true;  }
      if (null == x && null != y) { return false; }
      if (null != x && null == y) { return false; }
      return x.Equals(y);
    }

    public int GetHashCode(object obj)
    {
      if (null == obj) { return 0; }
      var x = obj as NameClass;
      if (null != x) { return x.GetHashCode(); }
      return obj.GetHashCode();
    }

    public int CompareTo(object obj)
    {
      if (obj == null) return 1;

      NameClass x = obj as NameClass;
      if (x == null) 
      {
        throw new ArgumentException("Object is not a NameClass");
      }
      return CompareTo(x);
    }
  }
4

5 に答える 5

5

仕組みDistinct:

Object.GetHashCode()オブジェクトの最初の比較に使用されるDistinctの実装は少なくともありませObject.GetHashCodeObject.Equals

正確には、Enumerable.Distinct(この IEnumerable ソース)を使用EqualityComparer<NameClass>.Defaultして最終的に等価性をチェックします (ハッシュ コードが一致しない場合、サンプルが機能しない理由である比較のその部分に到達しないことに注意してください)。

デフォルトの等値比較子である Default は、IEquatable ジェネリック インターフェイスを実装する型の値を比較するために使用されます。

EqualityComparer.DefaultIEquatable<T>は、実際には、直接フォールバックすることなくクラスを使用できますObject.Equals

Default プロパティは、型 T が System.IEquatable インターフェイスを実装しているかどうかを確認し、実装している場合は、その実装を使用する EqualityComparer を返します。それ以外の場合は、T によって提供される Object.Equals および Object.GetHashCode のオーバーライドを使用する EqualityComparer を返します。

したがって、基本的に機能するには、正しいバージョンの/Distinctが必要です。オプションですが、クラス内の の動作と一致する必要があります。EqualsGetHashCodeIEquatableGetHashCode


直し方:

あなたのサンプルにはpublic new int GetHashCode()メソッドがありpublic override int GetHashCode()ますEquals

public new int...「オーバーライド」を意味するのではなく、「古いメソッドを非表示にするメソッドの新しいバージョンを作成する」ことを意味することに注意してください。親オブジェクトへのポインターを介してメソッドを呼び出す呼び出し元には影響しません。

new個人的には、メソッドの定義に使用することはめったにないと思います。有用な場合のいくつかの提案は、new を使用してメソッドを非表示にするためのユースケースでカバーされています。

于 2013-07-11T19:44:47.303 に答える
3

インターフェイスを実装する必要はありません。メソッドを正しく実装するだけGetHashCodeですEquals

public class NameClass
{
    public NameClass(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }

    public override bool Equals(object obj)
    {
        var other = obj as NameClass;
        return other != null && other.Name == this.Name;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }
}
于 2013-07-11T19:47:12.780 に答える
2

Enumerable.Distinct<TSource> メソッド:

デフォルトの等値比較子Defaultを使用して値を比較します。

EqualityComparer.Default :

Defaultプロパティは、型 T が System.IEquatable<T> インターフェイスを実装しているかどうかを確認し実装されている場合は、その実装を使用する EqualityComparer<T> を返します。それ以外の場合は、 Tによって提供される Object.Equals および Object.GetHashCode のオーバーライドを使用する EqualityComparer<T> を返します。

IEquatable<T> インターフェイス:

IEquatable<T> を実装する場合は、Object.Equals(Object)およびGetHashCodeの基本クラスの実装もオーバーライドして、それらの動作が IEquatable<T>.Equals メソッドの動作と一致するようにする必要があります。

メソッドのオーバーライド:

オーバーライド修飾子は、継承されたメソッド、プロパティ、インデクサー、またはイベントの抽象実装または仮想実装を拡張または変更するために必要です。

したがって、コードは次のようになります。

public class NameClass : IEquatable<NameClass>
{
    public NameClass(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }

    // implement IEquatable<NameClass>
    public bool Equals(NameClass other)
    {
        return (other != null) && (Name == other.Name);
    }

    // override Object.Equals(Object)
    public override bool Equals(object obj)
    {
        return Equals(obj as NameClass);
    }

    // override Object.GetHashCode()
    public override GetHashCode()
    {
        return Name.GetHashCode();
    }
}
于 2013-07-11T19:54:05.093 に答える
1

したがって、最初に、DistinctドキュメントEqualityComparer<T>.Defaultに従って、カスタム等値比較子が提供されていない場合 (何も提供していない場合) を使用してオブジェクトを比較します。

EqualityComparer<T>.Defaultは、そのドキュメントに従って、オブジェクトが を実装しているかどうかを調べます。実装さIEquatable<T>れている場合は、Equals の実装を使用します。

型が実装されているかどうかに関係なくIEquatable<T>、メソッドをEqualityComparer<T>.Default 使用しobject.GetHashCodeてオブジェクトの has コードを取得しますIEquatable<T>、残念ながら、オブジェクトのGetHashCode実装もオーバーライドすることを強制するものではありません。あなたの場合、 を実装している間IEquatable<T>、コードはオブジェクトの実装をオーバーライドしませんGetHashCode

その結果、Distinct実際にはEqualsタイプに適切なメソッドを使用していますが、間違ったGetHashCodeメソッドを使用しています。オブジェクトをハッシュしていて、その型EqualsGetHashCode同期していない実装がある場合は常に、問題が発生します。何が起こっているかというと、どのようなハッシュ ベースのコレクションでも、2 つの「等しい」オブジェクトが異なるバケットに送信されるため、Equalsメソッドが互いに呼び出されるポイントに到達することさえありません。たまたま運が良く、ハッシュ コレクションがあり、オブジェクトが偶然同じバケットに送信された場合、そのEqualsメソッドは意図したものであるため、実際には機能しますが、その可能性は非常に低くなります。(この特定のケースでは、約 2/2147483647、または 9.3e-10.

new GetHashCodeでメソッドを提供していますがNameClass、それはオブジェクトの実装を隠しており、オーバーライドしていません。ではなくGetHashCode使用するように実装を変更すると、コードが機能します。overridenew

于 2013-07-11T20:06:01.153 に答える
0

サンプル コードをめちゃくちゃにしていたことに気付きました。私のクラスは、Object ではなく DependencyObject から派生しています。DependencyObject クラスが封印されているため、GetHashCode または Equals 関数をオーバーライドできません。

于 2013-07-11T20:05:26.777 に答える