Meh*_*dad 18 .net c# finalization cer
显然,Constrained Execution Region保证不适用于迭代器(可能是因为它们的实现方式和所有),但这是一个bug还是设计?[见下面的例子.]
关于CER与迭代器一起使用的规则是什么?
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
class Program
{
static bool cerWorked;
static void Main(string[] args)
{
try
{
cerWorked = true;
foreach (var v in Iterate()) { }
}
catch { System.Console.WriteLine(cerWorked); }
System.Console.ReadKey();
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
unsafe static void StackOverflow()
{
Big big;
big.Bytes[int.MaxValue - 1] = 1;
}
static System.Collections.Generic.IEnumerable<int> Iterate()
{
RuntimeHelpers.PrepareConstrainedRegions();
try { cerWorked = false; yield return 5; }
finally { StackOverflow(); }
}
unsafe struct Big { public fixed byte Bytes[int.MaxValue]; }
}
Run Code Online (Sandbox Code Playgroud)
Bri*_*eon 15
好吧,我不知道这是一个错误还是一个非常奇怪的边缘情况,其中没有设计CERs来处理.
所以这是相关的代码.
private static IEnumerable<int> Iterate()
{
RuntimeHelpers.PrepareConstrainedRegions();
try { cerWorked = false; yield return 5; }
finally { StackOverflow(); }
}
Run Code Online (Sandbox Code Playgroud)
当这个被编译并且我们尝试使用Reflector将其反编译成C#时,我们得到了这个.
private static IEnumerable<int> Iterate()
{
RuntimeHelpers.PrepareConstrainedRegions();
cerWorked = false;
yield return 5;
}
Run Code Online (Sandbox Code Playgroud)
现在等一下!反射器已将此全部搞砸了.这就是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
}
Run Code Online (Sandbox Code Playgroud)
请注意,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();
}
}
Run Code Online (Sandbox Code Playgroud)
这个叫StackOverflow神秘地转移到哪里?在m_Finally2()方法内部.
private void <>m__Finally2()
{
this.<>1__state = -1;
Program.StackOverflow();
}
Run Code Online (Sandbox Code Playgroud)
所以让我们仔细研究一下.我们现在PrepareConstainedRegions在一个try区块内而不是外面的区域内进行调用.我们的StackOverflow电话已从一个finally街区转移到一个try街区.
根据文档 PrepareConstrainedRegions必须立即在try块之前.所以假设如果放在其他任何地方它是无效的.
但是,即使C#编译器得到了正确的部分,事情仍然会被搞砸,因为try块不受限制.只有catch,finally和,fault块.你猜怎么着?那个StackOverflow电话从一个finally块移到一个try块!