Подтвердить что ты не робот

У С# пытались - наконец, 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]; }
}

(Код в основном украден из здесь.)

4b9b3361

Ответ 1

Ну, я не знаю, была ли эта ошибка или просто странный крайний случай, когда CER не были предназначены для обработки.

Итак, вот соответствующий код.

private static IEnumerable<int> Iterate()
{
    RuntimeHelpers.PrepareConstrainedRegions();
    try { cerWorked = false; yield return 5; }
    finally { StackOverflow(); }
}

Когда это компилируется и мы пытаемся декомпилировать его в С# с Reflector, мы получаем это.

private static IEnumerable<int> Iterate()
{
    RuntimeHelpers.PrepareConstrainedRegions();
    cerWorked = false;
    yield return 5;
}

Теперь подождите секунду! Отражатель все это привинчивается. Это то, что ИЛ действительно выглядит.

.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 
}

Обратите внимание, что на самом деле нет вызова PrepareConstrainedRegions, несмотря на то, что говорит Reflector. Так где он скрывается? Ну, это прямо в автоматическом сгенерированном методе 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.

В соответствии с документация PrepareConstrainedRegions должна немедленно предшествовать блоку try. Таким образом, предположение заключается в том, что оно неэффективно, если оно помещено где-либо еще.

Но, даже если компилятор С# получил эту часть, все будет по-прежнему запутываться, потому что блоки try не ограничены. Только блоки catch, finally и fault. И угадай что? Этот вызов StackOverflow переместился из блока finally в блок try!