有没有办法在迭代器函数或属性中的finally块内引用异常,允许try..finally而不是try..catch?
我不会用它来改变或搞乱控制流,但是希望能够在finally块中获得对异常的引用(如果有人被抛出),以便从中读取并且可能将内容添加到Data成员.
我理解由于编译器从迭代器生成类的性质,可能不可能/允许出于同样的原因,首先不允许在yield语句周围使用try..catch.但是我仍然希望有一些方法(甚至是丑陋的技巧)来控制异常.
简化示例:
IEnumerable<SomeClass> Something
get
{
try
{
throw new SomeException();
yield return new SomeClass();
}
finally
{
Exception ex = ... // <= TODO - get hold of the exception here [if one was thrown]...
}
}
Run Code Online (Sandbox Code Playgroud)
Dan*_*ker 18
这是一个非常有趣的问题.
回想一下,在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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以现在假设我们有这个:
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";
}
Run Code Online (Sandbox Code Playgroud)
我们希望能够将所有身体都包裹在try/中catch,这很遗憾.但我们能做的就是包装生成的序列:
public static IEnumerable<String> LoggingStringYielder()
{
return StringYielder().Catch(
(NastyException ex) =>
Console.WriteLine("Exception caught: " + ex.StackTrace));
}
Run Code Online (Sandbox Code Playgroud)
也就是说,我通过调用"raw" StringYielder方法得到一个序列,然后我将new Catch运算符应用于它,指定如果发生某种异常类型该怎么做.在这里,我将打印堆栈跟踪.
所以,如果我这样做:
foreach (var str in LoggingStringYielder())
Console.WriteLine(str);
Run Code Online (Sandbox Code Playgroud)
程序完成而不会崩溃,输出为:
apple
banana
Exception caught: at ConsoleApplication7.Program.<StringYielder>.. blah
Run Code Online (Sandbox Code Playgroud)
因此,虽然你不能在原始迭代器方法中尝试捕获代码,但现在可以将它"包装"在迭代器方法的外部.这就像是在每个代码之间注入代码的非侵入式方式yield return.
奖金更新!
对我最后一句话的措辞非常挑剔:
首先,您可以在第一个之前抛出yield return它,并且它的处理方式相同,因为该代码在第一次调用时执行MoveNext.所以"...... 每个...... 之前的代码......"比"...... 每个...... 之间的代码"更准确.
其次,a yield return可以接受必须被评估的表达式,并且可以在评估期间抛出.这应该被视为在yield return发生之前执行的代码,即使它在语法之后出现.
如何将可能生成异常的所有代码移动到嵌套的try/catch中:
IEnumerable<int> GetFoo()
{
for (int i = -10; i < 10; i++)
{
Exception ex = null;
try
{
int result = 0;
try
{
result = 10 / i;
}
catch (Exception e) // Don't normally do this!
{
ex = e;
throw;
}
yield return result;
}
finally
{
if (ex != null)
{
// Use ex here
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是,使用上面的模式,您可以在catch块中执行所需的所有操作,这样可以更简单 - 您可以摆脱周围的try/finally块.
| 归档时间: |
|
| 查看次数: |
1397 次 |
| 最近记录: |