为什么SequenceEqual采用IEqualityComparer而不是谓词?

时间:2014-04-15 15:20:18

标签: .net linq-to-objects

为什么Enumerable.SequenceEqual将其比较器视为IEqualityComparer?该算法似乎没有使用GetHashCode。为什么不采用Func<TSource, TSource, bool>谓词,类似于First采用Func<TSource, bool>的方式?

3 个答案:

答案 0 :(得分:1)

我很想说“因为”。

如果您查看其他类似方法(例如Enumerable.Distinct),他们也会在重载中使用IEqualityComparer

此外,IEqualityComparer是检查对象是否相等的“正确”方法。 Func<TSource, TSource, bool>不会检查是否相等,它会在此时检查对象是否足够类似于您的特定用途。

幸运的是,制作自己的扩展方法很容易。例如,MoreLinqDistinctBy的实现,您可以查看。

答案 1 :(得分:1)

现在.NET Core是开源的,我在GitHub上将此问题发布为issue。希望,这将转化为答案或改进。

更新 - 从GitHub问题中获取有关现有设计及其问题的可能理由的详细信息:

  

IEqualityComparer,尽管它的名字,确实做了两件事:哈希   并检查是否平等。 a之间唯一的行为差异   Func<TSource, TSource, bool>IEqualityComparer就是这样   IEqualityComparerGetHashCode方法。如果你想做的一切   检查序列是否相等,你需要IEqualityComparer   尽管如此,写下散列代码(这可能很难做得很好)   它可能永远不会被使用(但你不能指望它永远不会被使用   使用是因为SequenceEqual没有记录它不会使用它。

     

通常,您与SequenceEqual进行比较的类型会碰巧   一个或多个IEqualityComparer个伴随类型,以便它们可以   存储在散列容器中。也许这就是为什么IEqualityComparer   被选为参数。但是,也有很多次   当没有IEqualityComparer并且没有散列时   需求。在这些情况下,必须创建一个类并实现   GetHashCode是浪费。

答案 2 :(得分:0)

我并没有真正回答你的问题,但这里有重载:

    public static bool SequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second,
        Func<TSource, TSource, bool> predicate)
    {
        return first.SequenceEqual(second, new PredicateEqualityComparer<TSource>(predicate));
    }

    public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, Func<TKey, TKey, bool> predicate)
    {
        return outer.Join(inner, outerKeySelector, innerKeySelector, resultSelector, new PredicateEqualityComparer<TKey>(predicate));
    }

    public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer,
        IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector,
        Func<TOuter, IEnumerable<TInner>, TResult> resultSelector, Func<TKey, TKey, bool> predicate)
    {
        return outer.GroupJoin(inner, outerKeySelector, innerKeySelector, resultSelector,
            new PredicateEqualityComparer<TKey>(predicate));
    }

    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, Func<TKey, TKey, bool> predicate)
    {
        return source.GroupBy(keySelector, new PredicateEqualityComparer<TKey>(predicate));
    }

    public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(
        this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, TKey, bool> predicate)
    {
        return source.GroupBy(keySelector, elementSelector, new PredicateEqualityComparer<TKey>(predicate));
    }

    public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector,
        Func<TKey, TKey, bool> predicate)
    {
        return source.GroupBy(keySelector, resultSelector, new PredicateEqualityComparer<TKey>(predicate));
    }

    public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector,
        Func<TKey, IEnumerable<TElement>, TResult> resultSelector, Func<TKey, TKey, bool> predicate)
    {
        return source.GroupBy(keySelector, elementSelector, resultSelector, new PredicateEqualityComparer<TKey>(predicate));
    }

    public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source,
        Func<TSource, TSource, bool> predicate)
    {
        return source.Distinct(new PredicateEqualityComparer<TSource>(predicate));
    }

    public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TSource, bool> predicate)
    {
        return first.Union(second, new PredicateEqualityComparer<TSource>(predicate));
    }

    public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first,
        IEnumerable<TSource> second, Func<TSource, TSource, bool> predicate)
    {
        return first.Intersect(second, new PredicateEqualityComparer<TSource>(predicate));
    }

    public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second,
        Func<TSource, TSource, bool> predicate)
    {
        return first.Except(second, new PredicateEqualityComparer<TSource>(predicate));
    }

    public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, Func<TKey, TKey, bool> predicate)
    {
        return source.ToDictionary(keySelector, new PredicateEqualityComparer<TKey>(predicate));
    }

    public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, TKey, bool> predicate)
    {
        return source.ToDictionary(keySelector, elementSelector, new PredicateEqualityComparer<TKey>(predicate));
    }

    public static ILookup<TKey, TSource> ToLookup<TSource, TKey>(this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, Func<TKey, TKey, bool> predicate)
    {
        return source.ToLookup(keySelector, new PredicateEqualityComparer<TKey>(predicate));
    }

    public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value,
        Func<TSource, TSource, bool> predicate)
    {
        return source.Contains(value, new PredicateEqualityComparer<TSource>(predicate));
    }

实现了这个目标:

public class PredicateEqualityComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _predicate;

    public PredicateEqualityComparer(Func<T, T, bool> predicate)
    {
        _predicate = predicate;
    }

    public bool Equals(T a, T b)
    {
        return _predicate(a, b);
    }

    public int GetHashCode(T a)
    {
        return a.GetHashCode();
    }

}

我只测试了SequenceEqual。也许有些超载因为散列而无法按预期工作。这取决于你,想一想。