2009-05-27

Wykorzystanie nazw generowanych przez kompilator.

Mamy taki kod:
public partial class Form1 : Form
{
    public class TestValue
    {
        public int v { get; set; }

        public static TestValue operator +(TestValue l, TestValue r)
        {
            return new TestValue() { v = l.v + r.v };
        }
    }

    public Form1()
    {
        InitializeComponent();
        Test1();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        Close();
    }

    private void Test1()
    {
        Func<int> del = () => { return 12; };

        TestValue v1 = new TestValue();
        v1.v = 5;
        TestValue v2 = new TestValue() { v = 5 };
        TestValue v3 = v1 + v2;
    }
}
Po stronie IL wygląda to tak:
.method private hidebysig instance void Test1() cil managed
{
    .maxstack 3
    .locals init (
        [0] class [System.Core]System.Func`1<int32> del,
        [1] class TestApp.Form1/TestValue v1,
        [2] class TestApp.Form1/TestValue v2,
        [3] class TestApp.Form1/TestValue v3,
        [4] class TestApp.Form1/TestValue <>g__initLocal0)
    L_0000: nop 
    L_0001: ldsfld class [System.Core]System.Func`1<int32> TestApp.Form1::CS$<>9__CachedAnonymousMethodDelegate2
    L_0006: brtrue.s L_001b
    L_0008: ldnull 
    L_0009: ldftn int32 TestApp.Form1::<Test1>b__1()
    L_000f: newobj instance void [System.Core]System.Func`1<int32>::.ctor(object, native int)
    L_0014: stsfld class [System.Core]System.Func`1<int32> TestApp.Form1::CS$<>9__CachedAnonymousMethodDelegate2
    L_0019: br.s L_001b
    L_001b: ldsfld class [System.Core]System.Func`1<int32> TestApp.Form1::CS$<>9__CachedAnonymousMethodDelegate2
    L_0020: stloc.0 
    L_0021: newobj instance void TestApp.Form1/TestValue::.ctor()
    L_0026: stloc.1 
    L_0027: ldloc.1 
    L_0028: ldc.i4.5 
    L_0029: callvirt instance void TestApp.Form1/TestValue::set_v(int32)
    L_002e: nop 
    L_002f: newobj instance void TestApp.Form1/TestValue::.ctor()
    L_0034: stloc.s <>g__initLocal0
    L_0036: ldloc.s <>g__initLocal0
    L_0038: ldc.i4.5 
    L_0039: callvirt instance void TestApp.Form1/TestValue::set_v(int32)
    L_003e: nop 
    L_003f: ldloc.s <>g__initLocal0
    L_0041: stloc.2 
    L_0042: ldloc.1 
    L_0043: ldloc.2 
    L_0044: call class TestApp.Form1/TestValue TestApp.Form1/TestValue::op_Addition(class TestApp.Form1/TestValue, class TestApp.Form1/TestValue)
    L_0049: stloc.3 
    L_004a: ret 
}
Jak widać dla obsłużenia metod anonimowych kompilator generuje kilka metod. Gdybyśmy we wnętrzu metody anonimowej skorzystali ze zmiennych spoza jej zasięgu były by to klasy. Tych nazw nie możemy wykorzystać w naszym kodzie gdyż nie jesteśmy w stanie zadeklarować zmiennych o nazwach typu CS$<>9__CachedAnonymousMethodDelegate3, <>g__initLocal0, <Test1>b__2. Pozostaje nam kwestia metod typu op_Addition() i set_v(). Bezpośrednie ich użycie w kodzie spowoduje błąd kompilacji: cannot explicitly call operator or accessor Zaś zadeklarowanie metod o takich nazwach spowoduje błąd: Type 'TestValue' already reserves a member called 'set_v' with the same parameter types Ostatecznie gdyby nam się udało w jakimś innym przypadku użyć nazwa automatycznie generowanych przez kompilator, to nic nie stoi na przeszkodzie by kompilator wygenerował inne nazwy. W tym momencie nie mam wiedzy czy takie coś się zdarza. Dlaczego więc to nie działa dla nazw metod operatorów i właściwości. Obiekt skompilowany do kodu pośredniego może zostać wykorzystany np. jako assembly. Nikt nam nie broni nadpisać takiego obiektu. No i stajemy przed tym samym problemem, że nie możemy stworzyć funkcji o nazwie set_v, bo takowa już istnieje w sposób niejawny. Można by to pewnie obejść, ale to jest taka walka z problemami, które sami tworzymy. Najlepiej od początku zabronić używania takich nazw i zastrzec je. Być może istnieją jeszcze inne powody, ale na tą chwilę nic innego nie potrafię wymyślić.

Brak komentarzy:

Prześlij komentarz