Where
失敗を数えるあなた自身を転がすことを妨げるものは何もありません。ラムダもステートメントを含むメソッドもout/refパラメーターを参照できないため、 「何もありません」。そのため、次のシグネチャを持つ目的の拡張機能は機能しません。yield return
// dead-end/bad signature, do not attempt
IEnumerable<T> Where(
this IEnumerable<T> self,
Func<T,bool> predicate,
out int failures)
ただし、failure-countのローカル変数を宣言して、failure-countを取得できるaを返すFunc<int>
ことはできます。ローカル変数は、ラムダからの参照に対して完全に有効です。したがって、可能な(テスト済みの)実装は次のとおりです。
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());
上記のテスト、実行時の出力:
024
失敗=
2
警告しなければならないと感じる警告がいくつかあります。全体を歩かなくてもループから途中で抜け出す可能性があるため、失敗数は発生した失敗のみを反映し、 @ nneonneoのソリューション(私が好む)のIEnumerable<>
ように失敗の総数は反映しません。また、LINQの実装の場合拡張機能は、アイテムごとに述語を複数回呼び出す方法で変更された場合、失敗数は正しくありません。もう1つの興味深い点は、ループ本体内からgetFailureCount Funcを呼び出して、これまでの実行中の障害カウントを取得できるようにする必要があることです。Where
このソリューションを提示して、既存のパッケージ済みソリューションに縛られていないことを示しました。言語とフレームワークは、私たちのニーズに合わせてそれを拡張する多くの機会を私たちに提供します。