3

我们在LINQ Enumerable的代码审查中进行了一些自省,并发现了迭代 a 的不同策略以向我们提供一些关于它的信息。IEnumerable<TSource>

任何

public static bool Any<TSource>(
     this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
{
     if (source == null) throw Error.ArgumentNull("source");
     if (predicate == null) throw Error.ArgumentNull("predicate");
     foreach (TSource element in source) {
         if (predicate(element)) return true;
     }
     return false;
 }

 public static bool Any<TSource>(this IEnumerable<TSource> source)
 {
      if (source == null) throw Error.ArgumentNull("source");
      using (IEnumerator<TSource> e = source.GetEnumerator()) {
          if (e.MoveNext()) return true;
      }
      return false;
  }

数数

public static int Count<TSource>(this IEnumerable<TSource> source) 
{
    if (source == null) throw Error.ArgumentNull("source");
    ICollection<TSource> collectionoft = source as ICollection<TSource>;
    if (collectionoft != null) return collectionoft.Count;
    ICollection collection = source as ICollection;
    if (collection != null) return collection.Count;
    int count = 0;
    using (IEnumerator<TSource> e = source.GetEnumerator()) {
        checked {
            while (e.MoveNext()) count++;
        }
    }
    return count;
}

单一或默认

public static TSource SingleOrDefault<TSource>(
    this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
{
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    TSource result = default(TSource);
    long count = 0;
    foreach (TSource element in source) {
        if (predicate(element)) {
            result = element;
            checked { count++; }
        }
    }
    switch (count) {
        case 0: return default(TSource);
        case 1: return result;
    }
    throw Error.MoreThanOneMatch();
}

如您所见,使用了 3 种不同的策略。

  • Any:迭代枚举器,提前退出
  • Count:迭代枚举器,除非ICollection,调用Count
  • SingleOrDefault:迭代枚举器,不提前退出

一些观察:

  • 'Any()' 也可以针对 ICollection 进行优化
  • 'SingleOrDefault' 也可以进行优化以提前退出
  • 没有一个方法关心 IReadOnlyCollection,即使它也有一个属性 'Count'

问题

  • 这些策略的优缺点是什么?
  • LINQ 的实现是这样的,有充分的理由吗?
4

1 回答 1

1

嗯,前两种方法是不言自明的,不是吗?它们以尽快停止的方式进行了优化,并检查该类型是否具有Count避免循环的属性。

但是SingleOrDefaultwith 谓词确实有一个奇怪的实现。它可能会停在第二个匹配项,因为从那时起很明显InvalidOperationException必须抛出 a(Single...而不是First...确保最多有 1 个项目)。但相反,它会检查每个项目并计算匹配项。没有谓词的版本有这个优化。

所以问题是:到底是什么?看起来这确实是一个无法修复的错误,因为它只会在错误情况下降低性能。我不敢相信。

顺便说一句,同样的错误存在于Enumerable.Single.

于 2019-07-26T16:16:52.840 回答