【问题标题】:Why does SequenceEqual take an IEqualityComparer rather than a predicate?为什么 SequenceEqual 采用 IEqualityComparer 而不是谓词?
【发布时间】:2014-04-15 15:20:18
【问题描述】:

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

【问题讨论】:

  • 离题的 IMO,请参阅下面的我的 cmets。

标签: .net linq-to-objects


【解决方案1】:

我很想说“因为”。

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

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

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

【讨论】:

  • Distinct 需要 GetHashCode 以便它可以构建一个哈希表来进行多对多比较。其他取IEqualityComparer的方法类似。 SequenceEqual 是“不同的”:-) 因为它是一个简单的线性运算,不需要哈希表。
  • 目前我认为相等和足够相等之间没有任何真正的区别。
  • 我很不同意 :) SequenceEqual 两个可枚举的长度相同,可枚举中的对象顺序相同,并且它们相等。检查相等性的“正确”方法是相等比较器。如果您不关心“适当的相等性”,您可以设想一个 Enumerable.SequenceEqualEnough 传入两个 Func(一个用于 T,一个用于 S),它将选择“某物”来比较两个可枚举项。现在您甚至可以检查不同类型的可枚举是否“足够相等”。
  • 你能澄清一下你不同意的地方吗?我并不是说如果你碰巧有一个 IEqualityComparer 将不起作用或不应该得到支持。但是,如果您想比较没有序列的序列(例如,因为它们的对象从不存储在散列容器中),那么必须创建一个类来进行比较比简单的 lambda 方法要重得多。
  • 我没有直接看到散列与任何事情有什么关系。相等比较器用于 Equals 方法。如果您指的是 EqualityComparer.Default;如果您不指定一个,则用于比较器,如下所述:stackoverflow.com/questions/17224940/… 至于散列容器,嗯,有时它们是?
【解决方案2】:

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


更新 - 现有设计的可能原理及其问题的详细信息,取自 GitHub 问题:

IEqualityComparer,尽管它的名字,实际上做了两件事:哈希 并检查是否相等。唯一的行为差异 Func<TSource, TSource, bool>IEqualityComparerIEqualityComparer 有一个 GetHashCode 方法。如果你想做的一切 是检查序列相等性,IEqualityComparer 你必须 编写散列代码(这可能很难做好),即使 它可能永远不会被使用(但你不能指望它永远不会被使用 因为SequenceEqual 没有记录它不会使用它)。

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

【讨论】:

    【解决方案3】:

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

        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。由于散列,其中一些重载可能无法按预期工作。这取决于你,想清楚。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-26
      • 1970-01-01
      • 2014-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多