Е, не знам дали това е грешка или просто наистина странен крайен случай, в който 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;
}
Сега изчакайте само секунда! Рефлекторът е всичко това прецакано. Ето как всъщност изглежда IL.
.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. И така, къде се крие? Е, той е точно там в автоматично генерирания MoveNext
метод на IEnumerator
. Този път 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