没有什么(太多)可以阻止您滚动自己的 Where 来计算失败次数。 “没什么” 因为 lambdas 和带有 yield return 语句的方法都不允许引用 out/ref 参数,因此具有以下签名的所需扩展将不会'不工作:
// dead-end/bad signature, do not attempt
IEnumerable<T> Where(
this IEnumerable<T> self,
Func<T,bool> predicate,
out int failures)
但是,我们可以为失败计数声明一个局部变量并返回一个可以获取失败计数的Func<int>,并且局部变量对于从 lambdas 引用是完全有效的。因此,这是一个可能的(经过测试的)实现:
public static class EnumerableExtensions
{
public static IEnumerable<T> Where<T>(
this IEnumerable<T> self,
Func<T,bool> predicate,
out Func<int> getFailureCount)
{
if (self == null) throw new ArgumentNullException("self");
if (predicate == null) throw new ArgumentNullException("predicate");
int failures = 0;
getFailureCount = () => failures;
return self.Where(i =>
{
bool res = predicate(i);
if (!res)
{
++failures;
}
return res;
});
}
}
...下面是一些练习它的测试代码:
Func<int> getFailureCount;
int[] items = { 0, 1, 2, 3, 4 };
foreach(int i in items.Where(i => i % 2 == 0, out getFailureCount))
{
Console.WriteLine(i);
}
Console.WriteLine("Failures = " + getFailureCount());
上述测试,运行时输出:
0
2
4
失败次数 = 2
我觉得有一些注意事项有必要警告。由于您可能在没有走完整个IEnumerable<> 的情况下过早地跳出循环,因此失败计数只会反映遇到的失败,而不是像 @nneonneo 的解决方案(我更喜欢)中的失败总数。 /em> 另外,如果 LINQ 的 Where 扩展的实现更改为每个项目多次调用谓词,那么失败计数将不正确。另一个有趣的地方是,在您的循环体内,您应该能够调用 getFailureCount Func 以获取目前正在运行的失败计数。
我提出这个解决方案是为了表明我们并没有局限于现有的预打包解决方案。语言和框架为我们提供了很多机会来扩展它以满足我们的需求。