Ну, я не знаю, является ли это ошибкой или просто действительно странным крайним случаем, в котором 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
!