Ответ 1
Ну, я не знаю, была ли эта ошибка или просто странный крайний случай, когда CER не были предназначены для обработки.
Итак, вот соответствующий код.
private static IEnumerable<int> Iterate()
{
RuntimeHelpers.PrepareConstrainedRegions();
try { cerWorked = false; yield return 5; }
finally { StackOverflow(); }
}
Когда это компилируется и мы пытаемся декомпилировать его в С# с 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
. На этот раз рефлектор правильно справится.
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
. Таким образом, предположение заключается в том, что оно неэффективно, если оно помещено где-либо еще.
Но, даже если компилятор С# получил эту часть, все будет по-прежнему запутываться, потому что блоки try
не ограничены. Только блоки catch
, finally
и fault
. И угадай что? Этот вызов StackOverflow
переместился из блока finally
в блок try
!