关于 .Count() 的陷阱的简短而甜蜜的一般警告,以帮助将来偶然发现这篇文章的疲惫的旅行者!
短篇故事:
以下工作 - 毫无疑问 - 但如果可枚举没有得到方便/预先计算的“计数”的基础数组或列表的支持,则可能会有小的性能损失:
public bool IsValid
{
get { return SomeMethodReturningEnumerable().Count() <= threshold; } <--- small performance issue here
}
public IEnumerable<SomeObject> SomeMethodReturningEnumerable(){
yield return foo;
yield return bar; etc
}
对 .Count() 方法的调用可能会遍历可枚举中的每一项,然后将总计数与阈值进行比较。我们变得更聪明可以做得更好:
public bool IsValid
{
get { return !SomeMethodReturningEnumerable().HasMoreThan(threshold); } <--- neato!
}
public static bool HasLessThan<T>(this IEnumerable<T> sequence, int count) => !sequence.HasMoreThan(count - 1);
public static bool HasLessOrEqualTo<T>(this IEnumerable<T> sequence, int count) => !sequence.HasMoreThan(count);
public static bool HasMoreOrEqualTo<T>(this IEnumerable<T> sequence, int count) => sequence.HasMoreThan(count - 1);
public static bool HasMoreThan<T>(this IEnumerable<T> sequence, int count) => sequence.EnumerationCounterImpl(count, equals_vs_greaterThan: false);
public static bool HasExactly<T>(this IEnumerable<T> sequence, int count) => sequence.EnumerationCounterImpl(count, equals_vs_greaterThan: true);
public static bool EnumerationCounterImpl<T>(this IEnumerable<T> sequence, int count, bool equals_vs_greaterThan = true) //0
{
if (equals_vs_greaterThan && count < 0)
throw new ArgumentException($"{nameof(count)} is less than zero!");
if (!equals_vs_greaterThan && count < 0)
return true;
var staticCount = (sequence as ICollection)?.Count
?? (sequence as ICollection<T>)?.Count
?? (sequence as IReadOnlyCollection<T>)?.Count;
if (staticCount != null)
return staticCount > count;
using (var enumerator = sequence.GetEnumerator()) //1 optimization
{
for (int i = 0; i < count + 1; i++)
{
if (enumerator.MoveNext())
continue;
return false;
}
return !equals_vs_greaterThan // ==
|| enumerator.MoveNext(); // >
}
//0 https://blog.slaks.net/2015-01-12/linq-count-considered-occasionally-harmful/
//1 using the enumerator directly is slightly faster than using LINQ methods it avoids allocating an extra iterator
// state machine compared to using skip()
}
那里!问题再次解决,但这次我们注重性能!