c# - C# try-finally CERs 会在迭代器中中断吗?

标签 c# .net finalization cer

显然,受限执行区域保证不适用于迭代器(可能是因为它们是如何实现的),但这是一个错误还是设计使然? [参见下面的示例。]

即与迭代器一起使用 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 窃取的。)

最佳答案

好吧,我不知道这是一个错误还是只是一个真的奇怪的边缘案例,在这种情况下,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;
}

现在等一下! Reflector 把这一切都搞砸了。这就是 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。那么它潜伏在哪里呢?嗯,它就在自动生成的 IEnumeratorMoveNext 方法中。这次 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 block 内,而不是位于应有的位置之外。我们的 StackOverflow 调用已从 finally block 转移到 try block 。

根据documentation PrepareConstrainedRegions 必须紧接在 try block 之前。所以假设如果放在其他地方它是无效的。

但是,即使 C# 编译器得到了正确的部分,事情仍然会搞砸,因为 try block 不受约束。只有 catchfinallyfault block 是。你猜怎么着? StackOverflow 调用已从 finally block 移动到 try block !

关于c# - C# try-finally CERs 会在迭代器中中断吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6862205/

相关文章:

c# - 播放列表框中的项目

c# - .Net 应用程序的源代码真的可以这么简单地获取吗?

.net - 我需要在生成服务器上安装 Azure 2.7 SDK 的哪些部分?

c# - 使用 AutoMapper 将对象的属性映射到字符串

c# - 终结器线程 ID

c# - 如何在没有枚举的情况下访问 HashSet<TValue> 的引用值?

C# GetHashCode 问题

c# - Java 等价于 .NET 的 String.Format

fortran - 终结例程是否需要是基本的才能在超出范围的可分配数组的元素上调用?

java - 可终结对象的前期成本是多少?