在迭代器中执行c# try-finally cer中断
本文关键字:try-finally cer 中断 执行 迭代器 | 更新日期: 2023-09-27 18:04:05
显然,约束执行区域保证不适用于迭代器(可能是因为它们的实现方式),但这是一个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]; }
}
(代码大部分是从这里偷来的)
嗯,我不知道这是一个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
方法中。这次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
块!