次のコードがある場合:
List<MyClass> list = GetList();
list.ForEach(i => i.SomeMethod());
SomeMethod()
そして、例外をスローするとしましょう。繰り返しますかForEach
、それともそこで止まりますか?
終了した場合、コレクション内の残りのアイテムを取得してメソッドを実行する方法はありますか?
はい、例外がスローされた場合、ループは終了します。その動作が望ましくない場合は、デリゲートに例外処理を配置する必要があります。このためのラッパー メソッドを簡単に作成できます。
public static Action<T> SuppressExceptions<T>(Action<T> action)
{
return item =>
{
try
{
action(item);
}
catch (Exception e)
{
// Log it, presumably
}
};
}
正直なところ、可能であればこれを避けたいと思います。そのようなすべての例外をキャッチするのは不快です。また、失敗したアイテムや例外なども記録しません。要件についてもっと詳しく考える必要があります。
foreach
代わりに通常のループを使用し、エラーを処理してエラーを収集する別のメソッドを作成する方が、ほぼ確実にクリーンになります。個人的には、一般的にforeach
overを使用することを好みます。これに関する Eric Lippert の考えもForEach
読みたいと思うかもしれません。
エラーがスローされます。また、 の再実装も順調に進んでいますforeach
。ちょうどどうですか:
foreach (var item in list)
{
try
{
// dangerous thing with item
}
catch (Exception e)
{
// how do you want to log this?
}
}
これには、ほとんどのバージョンの .NET で動作し、明らかに副作用があるという利点があります。もちろん、このコードをForEach
デリゲートに直接入れることもできますが、(ラムダ関数ではなく) メソッド自体になる場合にのみお勧めします。
合理的な代替手段は、ForEachWithCatch
すべての例外をキャプチャして呼び出し元に送り返す独自の拡張機能を作成することです。
public static IEnumerable<Tuple<T,Exception>> ForEachWithCatch<T>(this IEnumerable<T> items, Action<T> action)
{
var exceptions = new List<Tuple<T,Exception>>();
foreach(var item in items)
{
try
{
action(item);
}
catch(Exception e)
{
exceptions.Add(Tuple.Create(item, e));
}
}
return exceptions;
}
これにより、失敗した各項目の列挙型とそれに対応する例外が返されます。
SomeMethod
try-catchブロックを実装している場合、 foreachは続行されます
void SomeMethod()
{
try
{
//Some operation on i
}
catch(Exception ex)
{
}
}
しかし、そうでない場合、foreachは壊れます。
それを行うことの1つはこの方法です
list.ForEach(i =>
{
try
{
i.SomeMethod();
}
catch(Exception ex)
{
}
});
ただし、コードにtry-catchブロックをゼロにすることは常に良いことです。そうでなければ、犯人がどこにいるのかを見つけることはできません。