は考えてみましょう: C#はどのようにして構造体のインタフェースメソッドを呼び出すのですか?
interface I { void M(); }
struct S: I { public void M() {} }
// in Main:
S s;
I i = s;
s.M();
i.M();
を、メイン用IL:
.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
ファースト(IL_000a
)、S::M()
がthis
のための値の型と呼ばれています。次の(IL_0011
)、参照(ボックス)タイプで呼び出されます。
これはどのように機能しますか?
私は、以下の3つの方法を考えることができます:I::M
の
- 2つのバージョンが値/ refのタイプのために、コンパイルされます。 vtableには、ref型のものが格納されますが、静的にディスパッチされた呼び出しは、値型用のものを使用します。 これは醜い可能性は低いですが、可能です。
- vtableには、
this
をアンボックスした「ラッパー」メソッドが格納され、実際のメソッドが呼び出されます。 これは、すべてのメソッドの引数が2回の呼び出しでコピーされなければならないため、非効率的です。 callvirt
にこれをチェックする特別なロジックがあります。 さらに効率が悪い:すべてcallvirt
は、(わずかな)ペナルティを負う。
注:IL_0002:ボックスSです。構造体は、呼び出しを許可するために囲まれています。ジェネリック医薬品を使ってその周りに道がありますが。 – Joey
@Joeyこれは '私は= sの;';最初の呼び出しは値型 'ldloca.s s'を使用します。 – valtron
これは複雑であり、ILを見ても全く助けにはなりません。 CLRのディスパッチスタブの使用方法の基本的な理解が必要です。 CLRチームのVance Morrisonは[このブログの記事](https://blogs.msdn.microsoft。com/vancem/2006/03/13/net-framework-stub-based-dispatch /でインタフェースコールを呼び出す –