Bedenken Sie: Wie handhabt C# den Aufruf einer Schnittstellenmethode in einer Struktur?
interface I { void M(); }
struct S: I { public void M() {} }
// in Main:
S s;
I i = s;
s.M();
i.M();
Und die IL für Main:
.maxstack 1
.entrypoint
.locals init (
[0] valuetype S s,
[1] class I i
)
IL_0000: nop
IL_0001: ldloc.0
IL_0002: box S
IL_0007: stloc.1
IL_0008: ldloca.s s
IL_000a: call instance void S::M()
IL_000f: nop
IL_0010: ldloc.1
IL_0011: callvirt instance void I::M()
IL_0016: nop
IL_0017: ret
First (IL_000a
), S::M()
mit einem Werttyp für this
genannt wird. Next (IL_0011
), wird es mit einem Referenz (Boxed) -Typ aufgerufen.
Wie funktioniert das?
Ich denke an drei Möglichkeiten können:
- Zwei Versionen von
I::M
kompiliert werden, für Wert/ref Typ. In der vtable speichert es die eine für ref-Typ, aber statisch abgesetzte Aufrufe verwenden die eine für Werttypen. Das ist hässlich und unwahrscheinlich, aber möglich. - In der Vtable speichert es eine "Wrapper" -Methode, die
this
ausbindet, ruft dann die eigentliche Methode auf. Das klingt ineffizient, weil alle Argumente der Methode durch zwei Aufrufe kopiert werden müssten. - Es gibt spezielle Logik, die dies in
callvirt
überprüft. Noch ineffizienter: Allecallvirt
s verursachen einen (leichten) Nachteil.
Hinweis 'IL_0002: Box S'. Die Struktur ist eingerahmt, um den Aufruf zu ermöglichen. Zwar gibt es einen Weg mit Generika. – Joey
@Joey das ist für 'I i = s;'; der erste Aufruf verwendet den Werttyp 'ldloca.s s' – valtron
Es ist verschachtelt und das Betrachten der IL hilft überhaupt nicht. Ein grundlegendes Verständnis der Verwendung von Dispatch-Stubs in der CLR ist erforderlich. Vance Morrison vom CLR-Team erklärt es ziemlich gut in [diesem Blogpost] (https://blogs.msdn.microsoft.com).com/vancem/2006/03/13/digeing-in-interface-Aufrufe-im-Netzwerk-Framework-stub-based-dispatch /). –