首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C# try-finally CER会中断迭代器吗?

C# try-finally CER会中断迭代器吗?
EN

Stack Overflow用户
提问于 2011-07-29 00:16:15
回答 1查看 510关注 0票数 18

显然,受限执行区域保证不适用于迭代器(可能是因为它们的实现方式等等),但这是一个bug还是设计?请参见下面的示例。

也就是说,使用迭代器的CER的规则是什么?

代码语言:javascript
复制
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窃取的。)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-07-29 01:24:15

好吧,我不知道这是一个bug,还是仅仅是一个非常奇怪的边缘案例,CER在设计上不能处理。

所以这是相关的代码。

代码语言:javascript
复制
private static IEnumerable<int> Iterate()
{
    RuntimeHelpers.PrepareConstrainedRegions();
    try { cerWorked = false; yield return 5; }
    finally { StackOverflow(); }
}

当这个代码被编译后,我们尝试用Reflector将它反编译成C#,结果是这样的。

代码语言:javascript
复制
private static IEnumerable<int> Iterate()
{
    RuntimeHelpers.PrepareConstrainedRegions();
    cerWorked = false;
    yield return 5;
}

现在稍等片刻!反射器把这一切都搞砸了。这就是IL的实际外观。

代码语言:javascript
复制
.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。那么它潜伏在哪里呢?嗯,它就在自动生成的IEnumeratorMoveNext方法中,这一次反射器做对了。

代码语言:javascript
复制
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()方法内部。

代码语言:javascript
复制
private void <>m__Finally2()
{
    this.<>1__state = -1;
    Program.StackOverflow();
}

因此,让我们更仔细地研究一下。我们现在将PrepareConstainedRegions调用放在try块内部,而不是应该在外部的地方。并且我们的StackOverflow调用已经从finally块移动到了try块。

根据documentationPrepareConstrainedRegions必须紧跟在try块之前。因此,假设它放在其他任何地方都是无效的。

但是,即使C#编译器正确地完成了这一部分,因为try块不受约束,所以事情仍然会搞砸。只有catchfinallyfault块。猜猜发生了什么?那个StackOverflow调用从finally块移到了try块!

票数 15
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6862205

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档