Considerare: Come gestisce C# chiamando un metodo di interfaccia su una struttura?
interface I { void M(); }
struct S: I { public void M() {} }
// in Main:
S s;
I i = s;
s.M();
i.M();
E la IL per principale:
.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()
si chiama con un tipo di valore per this
. Successivamente (IL_0011
), viene chiamato con un tipo di riferimento (in scatola).
Come funziona?
Mi vengono in mente tre modi:
- due versioni
I::M
sono compilati, per il valore di tipo/ref. Nel vtable, memorizza quello per il tipo di ref, ma le chiamate inviate staticamente usano quella per i tipi di valore. Questo è brutto e improbabile, ma possibile. - In vtable, memorizza un metodo "wrapper" che cancella lo
this
, quindi chiama il metodo effettivo. Sembra inefficiente perché tutti gli argomenti del metodo dovrebbero essere copiati attraverso due chiamate. - C'è una logica speciale che verifica questo in
callvirt
. Ancora più inefficiente: tutti glicallvirt
s incorrono in una (leggera) penalità.
Nota 'IL_0002: casella S'. La struttura è in scatola per consentire la chiamata. Anche se c'è un modo per aggirare questo con i generici. – Joey
@Joey che è per 'I i = s;'; la prima chiamata usa il tipo di valore 'ldloca.s s' – valtron
È contorto e guardare l'IL non aiuta affatto. È richiesta una comprensione di base del modo in cui vengono utilizzati gli invii di invio nel CLR. Vance Morrison del team CLR lo spiega abbastanza bene in [questo post del blog] (https://blogs.msdn.microsoft.com/vancem/2006/03/13/scavo-into-interfaccia-chiamate-in-a base-net-quadro-stub-spedizione /). –