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