Что ж, я не знаю, является ли это ошибкой или просто действительно странным пограничным случаем, для обработки которого CER не предназначены.
Итак, вот соответствующий код.
private static IEnumerable<int> Iterate()
{
RuntimeHelpers.PrepareConstrainedRegions();
try { cerWorked = false; yield return 5; }
finally { StackOverflow(); }
}
Когда это компилируется и мы пытаемся декомпилировать его в C# с помощью Reflector, мы получаем это.
private static IEnumerable<int> Iterate()
{
RuntimeHelpers.PrepareConstrainedRegions();
cerWorked = false;
yield return 5;
}
Теперь подождите секунду! Рефлектор все испортил. Вот так на самом деле выглядит ИЖ.
.method private hidebysig static class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> Iterate() cil managed
{
.maxstack 2
.locals init (
[0] class Sandbox.Program/<Iterate>d__1 d__,
[1] class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> enumerable)
L_0000: ldc.i4.s -2
L_0002: newobj instance void Sandbox.Program/<Iterate>d__1::.ctor(int32)
L_0007: stloc.0
L_0008: ldloc.0
L_0009: stloc.1
L_000a: br.s L_000c
L_000c: ldloc.1
L_000d: ret
}
Обратите внимание, что на самом деле никакого вызова PrepareConstrainedRegions
нет, несмотря на то, что говорит Reflector. Так где же он прячется? Ну, это прямо там, в автоматически сгенерированном методе IEnumerator
MoveNext
. На этот раз Reflector делает все правильно.
private bool MoveNext()
{
try
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
RuntimeHelpers.PrepareConstrainedRegions();
this.<>1__state = 1;
Program.cerWorked = false;
this.<>2__current = 5;
this.<>1__state = 2;
return true;
case 2:
this.<>1__state = 1;
this.<>m__Finally2();
break;
}
return false;
}
fault
{
this.System.IDisposable.Dispose();
}
}
И куда таинственно переместился этот призыв к StackOverflow
? Прямо внутри метода m_Finally2()
.
private void <>m__Finally2()
{
this.<>1__state = -1;
Program.StackOverflow();
}
Итак, давайте рассмотрим это немного подробнее. Теперь у нас есть вызов PrepareConstainedRegions
внутри блока try
, а не снаружи, где он должен быть. И наш вызов StackOverflow
переместился из блока finally
в блок try
.
Согласно документации, PrepareConstrainedRegions
должен стоять непосредственно перед блоком try
. Таким образом, предполагается, что он неэффективен, если его разместить где-либо еще.
Но даже если бы компилятор C# понял эту часть правильно, все равно все было бы испорчено, потому что блоки try
не ограничены. Доступны только блоки catch
, finally
и fault
. И угадайте, что? Этот вызов StackOverflow
был перемещен из блока finally
в блок try
!
person
Brian Gideon
schedule
28.07.2011
yield return
вtry-catch
. Просто говорю... - person Brian Gideon   schedule 29.07.2011