12

多くの IEqualityComparers の実装にやや怠惰であり、比較対象のオブジェクトのクラス実装を簡単に編集できないことを考えると、Distinct() および Except() 拡張メソッドで使用することを意図した次の方法を使用しました。:

public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
    Func<T, T, bool> compareFunction;
    Func<T, int> hashFunction;

    public GenericEqualityComparer(Func<T, T, bool> compareFunction, Func<T, int> hashFunction)
    {
        this.compareFunction = compareFunction;
        this.hashFunction = hashFunction;
    }

    public bool Equals(T x, T y)
    {
        return compareFunction(x, y);
    }

    public int GetHashCode(T obj)
    {
        return hashFunction(obj);
    }
}

いいように思えますが、本当に必要なたびにハッシュ関数を与えることはありますか? オブジェクトをバケットに入れるためにハッシュコードが使用されることを理解しています。異なるバケット、オブジェクトは等しくなく、equal は呼び出されません。

GetHashCode が同じ値を返す場合、equals が呼び出されます。( from : Equals メソッドがオーバーライドされたときに GetHashCode をオーバーライドすることが重要なのはなぜですか? )

では、たとえば (多くのプログラマーが恐怖で叫んでいるのを聞いています)、 GetHashCode が定数を返し、 Equal の呼び出しを強制する場合、何が問題になる可能性がありますか?

4

6 に答える 6

13

何も問題はありませんが、ハッシュ テーブル ベースのコンテナーでは、ルックアップを行うときに約 O(1) から O(n) のパフォーマンスになります。単純にすべてを List に格納し、力ずくで同等を満たす項目を検索する方がよいでしょう。

于 2011-05-06T09:18:59.320 に答える
10

一般的なユースケースが、プロパティの1つに従ってオブジェクトを比較する場合は、コンストラクターを追加して、次のように実装および呼び出すことができます。

public GenericEqualityComparer(Func<T, object> projection)
{
    compareFunction = (t1, t2) => projection(t1).Equals(projection(t2));
    hashFunction = t => projection(t).GetHashCode();
}

var comaparer = new GenericEqualityComparer( o => o.PropertyToCompare);

これにより、プロパティによって実装されたハッシュが自動的に使用されます。

編集:より効率的で堅牢な実装は、私のマークのコメントに影響を与えました:

public static GenericEqualityComparer<T> Create<TValue>(Func<T, TValue> projection)
{
    return new GenericEqualityComparer<T>(
        (t1, t2) => EqualityComparer<TValue>.Default.Equals( projection(t1), projection(t2)),
        t => EqualityComparer<TValue>.Default.GetHashCode(projection(t)));
}

var comparer = GenericEqualityComparer<YourObjectType>.Create( o => o.PropertyToCompare); 
于 2011-05-06T09:27:29.480 に答える
1

CodeProject でこれを見つけました - A Generic IEqualityComparer for Linq Distinct()うまくできました。

使用事例:

IEqualityComparer<Contact> c =  new PropertyComparer<Contact>("Name");
IEnumerable<Contact> distinctEmails = collection.Distinct(c); 

ジェネリック IEqualityComparer

public class PropertyComparer<T> : IEqualityComparer<T>
{
    private PropertyInfo _PropertyInfo;

    /// <summary>
    /// Creates a new instance of PropertyComparer.
    /// </summary>
    /// <param name="propertyName">The name of the property on type T 
    /// to perform the comparison on.</param>
    public PropertyComparer(string propertyName)
    {
        //store a reference to the property info object for use during the comparison
        _PropertyInfo = typeof(T).GetProperty(propertyName, 
    BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
        if (_PropertyInfo == null)
        {
            throw new ArgumentException(string.Format("{0} 
        is not a property of type {1}.", propertyName, typeof(T)));
        }
    }

    #region IEqualityComparer<T> Members

    public bool Equals(T x, T y)
    {
        //get the current value of the comparison property of x and of y
        object xValue = _PropertyInfo.GetValue(x, null);
        object yValue = _PropertyInfo.GetValue(y, null);

        //if the xValue is null then we consider them equal if and only if yValue is null
        if (xValue == null)
            return yValue == null;

        //use the default comparer for whatever type the comparison property is.
        return xValue.Equals(yValue);
    }

    public int GetHashCode(T obj)
    {
        //get the value of the comparison property out of obj
        object propertyValue = _PropertyInfo.GetValue(obj, null);

        if (propertyValue == null)
            return 0;

        else
            return propertyValue.GetHashCode();
    }

    #endregion
}  
于 2014-03-05T13:12:16.567 に答える
1

あなたのパフォーマンスは無駄になります。セットデータ構造に実装するDistinctと効率的な操作になります。Except一定のハッシュ値を提供することにより、基本的にこの特性を破壊し、線形検索を使用して単純なアルゴリズムを強制します。

これがデータ量に許容できるかどうかを確認する必要があります。しかし、データセットがやや大きい場合、違いは顕著になります。たとえば、Except予想時間 O( n ) から O( n ²) に増加しますが、これは大きな問題になる可能性があります。

定数を提供するのではなく、オブジェクトの独自のGetHashCodeメソッドを呼び出してみませんか? GetHashCode特に良い値を与えるわけではありませんが、定数を使用するよりも悪いことはありません。オブジェクトのメソッドがオーバーライドされて間違った値を返さない限り、正確性は維持されます。

于 2011-05-06T09:22:20.520 に答える
-1

このコードを試してください:

public class GenericCompare<T> : IEqualityComparer<T> where T : class
{
    private Func<T, object> _expr { get; set; }
    public GenericCompare(Func<T, object> expr)
    {
        this._expr = expr;
    }
    public bool Equals(T x, T y)
    {
        var first = _expr.Invoke(x);
        var sec = _expr.Invoke(y);
        if (first != null && first.Equals(sec))
            return true;
        else
            return false;
    }
    public int GetHashCode(T obj)
    {
        return obj.GetHashCode();
    }
}

例: collection = collection.Except(ExistedDataEles, new GenericCompare(x=>x.Id)).ToList();

于 2014-05-13T06:12:23.990 に答える