问题:
你向迭代器中添加了一个try-finally语句块,并且注意到finally块并没有按所想的那样执行。
解决方案:
在GetEnumerator迭代器中用一个try块包围迭代代码,并在该try块后接一个finally块,代码如下所示:
public static void TestFinallyAndIterators()
{
//Create a StringSet object and fill it with data
StringSet strSet =
new StringSet()
{"item1",
"item2",
"item3",
"item4",
"item5"};
// Use the GetEnumerator iterator.
foreach (string s in strSet)
Console.WriteLine(s);
}
运行这段代码时,将显示以下输出:
item1
item2
item3
item4
item5
In iterator finally block
讨论:
你可能认为这段代码的输出将在显示strSet对象中的每个数据项之后显示 In iterator finally block字符串。不过,这不是在迭代器中处理finally块的方式。仅当迭代完成之后,代码执行离开foreach循环时(比如,当遇到break、return或throw块语句时),或者当执行yield break语句时,才会调用与迭代器成员体内包含yield返回语句的try块关联的所有finally块,从而有效地终止迭代器。
为了查看迭代器如何处理catch和finally语句块(注意,包含yield语句的try块内不能包含catch块),思考一下代码:
public static void TestFinallyAndIterators()
{
//Create a StringSet object and fill it with data
StringSet strSet =
new StringSet()
{"item1",
"item2",
"item3",
"item4",
"item5"};
// Display all data in StringSet object
try
{
foreach (string s in strSet)
{
try
{
Console.WriteLine(s);
// Force an exception here
//throw new Exception();
}
catch (Exception)
{
Console.WriteLine("In foreach catch block");
}
finally
{
// Executed on each iteration
Console.WriteLine("In foreach finally block");
}
}
}
catch (Exception)
{
Console.WriteLine("In outer catch block");
}
finally
{
// Executed on each iteration
Console.WriteLine("In outer finally block");
}
/*
This code is executed in this fashion when an exception occurs in the iterator:
- In iterator finally block
- In outer catch block
- In outer finally block
This code is executed in this fashion when NO exception occurs in the iterator:
- item1
- In foreach finally block
- item2
- In foreach finally block
- item3
- In foreach finally block
- item4
- In foreach finally block
- item5
- In foreach finally block
- item6
- In foreach finally block
- In iterator finally block
- In outer finally block
}
我们看到,每次迭代都会执行foreach循环内的finally语句块。不过,仅当所有迭代都完成后,才会执行迭代器内的finally语句块。另外,注意到迭代器的finally语句块将会包装foreach循环的finally语句块之前执行。
如果在处理第二个元素期间,迭代器自身发生异常,那么将会显示如下输出;
item1 In foreach finally block
(Exception occurs here...)
In foreach catch block
In foreach finally block
In iterator finally block
In outer finally block
注意,在这种情况下,首先执行foreach循环内的catch和finally语句块,然后执行迭代器的finally语句块,最后执行外层finally语句块。
理解迭代器内的catch和finally语句块的工作方式将帮助你在正确的位置添加catch和finally语句块。如果需要在迭代完成之后立即执行一次finally语句块,可以将该finally语句块添加到迭代器方法中。不过,如果希望每次迭代都执行finally语句块,就需要将finally语句块置于foreach循环体内。
如果需要在迭代器异常发生之后立即捕获他们,就应该考虑将foreach循环包装在一个try-catch语句块中。foreach循环内的任何try-catch语句块都将错过从迭代器引发的异常。
参考:
MSDN文档中的“try-catch”“迭代器”“yield”“IEnumerator接口”和”IEnumerable接口”主题。