显然,受限执行区域保证不适用于迭代器(可能是因为它们的实现方式等等),但这是一个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]; }
}(代码大多是从here窃取的。)
发布于 2011-07-29 01:24:15
好吧,我不知道这是一个bug,还是仅仅是一个非常奇怪的边缘案例,CER在设计上不能处理。
所以这是相关的代码。
private static IEnumerable<int> Iterate()
{
RuntimeHelpers.PrepareConstrainedRegions();
try { cerWorked = false; yield return 5; }
finally { StackOverflow(); }
}当这个代码被编译后,我们尝试用Reflector将它反编译成C#,结果是这样的。
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
}请注意,尽管Reflector说了什么,但实际上并没有调用PrepareConstrainedRegions。那么它潜伏在哪里呢?嗯,它就在自动生成的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块。
根据documentation,PrepareConstrainedRegions必须紧跟在try块之前。因此,假设它放在其他任何地方都是无效的。
但是,即使C#编译器正确地完成了这一部分,因为try块不受约束,所以事情仍然会搞砸。只有catch、finally和fault块。猜猜发生了什么?那个StackOverflow调用从finally块移到了try块!
https://stackoverflow.com/questions/6862205
复制相似问题