18

どうやら、制約付き実行領域の保証はイテレーターには適用されません(おそらく、それらがどのように実装されているかなどが原因です)が、これはバグですか、それとも設計によるものですか?[以下の例を参照してください。]

つまり、イテレータで使用される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]; }
}

(コードは主にここから盗まれました。)

4

1 に答える 1

15

まあ、これがバグなのか、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 
}

PrepareConstrainedRegions実際、 Reflectorが言っていることにもかかわらず、呼び出しがないことに注意してください。それで、それはどこに潜んでいますか?まあ、それは自動生成された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そして、私たちのStackOverflow呼び出しはfinallyブロックからブロックに移動しましたtry

ドキュメント によると、ブロックPrepareConstrainedRegionsの直前にある必要があります。tryしたがって、他の場所に配置すると効果がないという前提があります。

ただし、C#コンパイラがその部分を正しく取得したとしても、tryブロックが制約されていないため、問題が発生します。catch、、、finallyおよびfaultブロックのみです。そして、何を推測しますか?そのStackOverflow呼び出しはfinallyブロックからブロックに移動しましたtry

于 2011-07-28T17:24:15.400 に答える