没有什么(太多)可以阻止您自己滚动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 的解决方案(我更喜欢)中的失败总数。另外,如果 LINQ 的实现Where
扩展名被更改为每个项目多次调用谓词的方式,那么失败计数将不正确。另一个有趣的地方是,在您的循环体内,您应该能够调用 getFailureCount Func 以获取目前正在运行的失败计数。
我提出这个解决方案是为了表明我们没有被锁定在现有的预打包解决方案中。语言和框架为我们提供了很多机会来扩展它以满足我们的需求。