这是一个非常有趣的问题。
回想一下,在Linq中,提供了许多标准运算符来有效地链接在一起。目前还没有一个运算符可以让您在内部序列周围包装自定义异常处理。
因此,我的建议是编写一个新的运算符,允许您指定一个操作,以处理在执行 IEnumerator.MoveNext
期间发生的任何异常:
public static class EnumerableExceptions
{
public static IEnumerable<TItem> Catch<TItem, TEx>(
this IEnumerable<TItem> source,
Action<TEx> handler) where TEx : Exception
{
using (var enumerator = source.GetEnumerator())
{
for (; ; )
{
try
{
if (!enumerator.MoveNext())
yield break;
}
catch (TEx x)
{
handler(x);
yield break;
}
yield return enumerator.Current;
}
}
}
}
假设我们现在有这个:
public class NastyException : Exception { }
public static IEnumerable<String> StringYielder()
{
yield return "apple";
yield return "banana";
throw new NastyException();
yield return "oracle";
yield return "grapefruit";
yield return "microsoft";
}
我们希望能够用try
/catch
来包装整个主体,但不幸的是这是非法的。但是我们可以包装生成的序列:
public static IEnumerable<String> LoggingStringYielder()
{
return StringYielder().Catch(
(NastyException ex) =>
Console.WriteLine("Exception caught: " + ex.StackTrace));
}
那就是说,我通过调用“原始”
StringYielder
方法来获取一个序列,然后对其应用新的
Catch
运算符,指定在发生某种异常类型时要执行什么操作。这里我只打印堆栈跟踪信息。
因此,如果我这样做:
foreach (var str in LoggingStringYielder())
Console.WriteLine(str)
程序完成而不崩溃,输出如下:
apple
banana
Exception caught: at ConsoleApplication7.Program.<StringYielder>.. blah
虽然你不能在原始迭代器方法内部使用try catch,但是现在你可以将其“包装”在迭代器方法的外部。这就像一种非侵入式的方式,在每个yield return
之间注入异常处理代码。
额外更新!
如果对我上一句话的措辞要求严格一些: