ここで間違えたらやめてください。
私の理解が正しければ、クラスのインスタンスでメソッドを呼び出すと、JIT コンパイラはインスタンスの型に対応する型オブジェクトを見つけ、その中の実際のメソッド コードへの参照を見つけます。
私の質問は、これが値型に対してどのように機能するかです。値型には、参照型のような型オブジェクト ポインターがないという印象を受けました。この場合、メソッド コードが呼び出されたときに、CLR はどのようにしてメソッド コードに移動するのでしょうか。
ここで間違えたらやめてください。
私の理解が正しければ、クラスのインスタンスでメソッドを呼び出すと、JIT コンパイラはインスタンスの型に対応する型オブジェクトを見つけ、その中の実際のメソッド コードへの参照を見つけます。
私の質問は、これが値型に対してどのように機能するかです。値型には、参照型のような型オブジェクト ポインターがないという印象を受けました。この場合、メソッド コードが呼び出されたときに、CLR はどのようにしてメソッド コードに移動するのでしょうか。
例を考えてみましょう。次の構造があるとします。
public struct Test
{
public void TestMethod()
{
}
}
そのためのILコードは次のとおりです。
.class public sequential ansi sealed beforefieldinit ConsoleApplication.Test
extends [mscorlib]System.ValueType
{
.pack 0
.size 1
.method public hidebysig
instance void TestMethod () cil managed
{
// Method begins at RVA 0x21dc
// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method Test::TestMethod
}
さて、C# コンパイラは の型を静的に認識しているため、オーバーロードの解決を行い、正確に呼び出されたものTest
を見つけることができます。TestMethod
次に、MSIL を発行して引数を MSIL 仮想スタックにプッシュし、型へのポインターのパラメーターを期待しますTest
。これは、コンパイラーがボックス化せずに処理し、その特定のメソッドへのメタデータ参照を含む呼び出し命令を発行します。
.locals init (
[0] valuetype ConsoleApplication.Test test
)
IL_0000: ldloca.s test
IL_0002: initobj ConsoleApplication.Test
IL_0008: ldloca.s test
IL_000a: call instance void ConsoleApplication.Test::TestMethod()
ForToString
およびGetHashCode
コンパイラはConstrained OpCodeを使用します。これは、これらのメソッドがオーバーロードされる可能性があるためです。
IL_0002: initobj ConsoleApplication.Test
IL_0008: ldloca.s test
IL_000a: constrained. ConsoleApplication.Test
IL_0010: callvirt instance int32 [mscorlib]System.Object::GetHashCode()
制約付きオペコードにより、IL コンパイラは、ptr が値型か参照型かに関係なく、統一された方法で仮想関数を呼び出すことができます。制約付きプレフィックスを使用すると、値の型に関する潜在的なバージョン管理の問題も回避できます。制約付きプレフィックスを使用しない場合、値の型が System.Object のメソッドをオーバーライドするかどうかに応じて、異なる IL を発行する必要があります。たとえば、値型 V が Object.ToString() メソッドをオーバーライドする場合、call V.ToString() 命令が発行されます。そうでない場合は、box 命令と callvirt Object.ToString() 命令が発行されます。前者の場合、オーバーライドが後で削除された場合、後者の場合、オーバーライドが後で追加された場合、バージョン管理の問題が発生する可能性があります。
GetType
メソッドは非仮想であり、Object
型で定義されているため、ボクシングが必要です。
IL_0002: initobj ConsoleApplication.Test
IL_0008: ldloc.0
IL_0009: box ConsoleApplication.Test
IL_000e: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()