Вызов методов для типов значений
Останови меня, если я ошибусь здесь.
Если я правильно понимаю, когда я вызываю метод для экземпляра класса, JIT-компилятор находит объект типа, соответствующий типу экземпляра, а затем находит в нем ссылку на фактический код метода.
Мой вопрос: как это работает для типов значений? У меня сложилось впечатление, что у типов значений нет указателя на объект типа, как у ссылочных типов. Если это так, как CLR удается перейти к коду метода при его вызове?
2 ответа
Рассмотрим пример, предположим, у нас есть следующая структура:
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()
За ToString
а также GetHashCode
компилятор использует ограниченный код операции, потому что эти методы могут быть перегружены.
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 типом значения или ссылочным типом. Использование ограниченного префикса также позволяет избежать потенциальных проблем управления версиями с типами значений. Если ограниченный префикс не используется, должен выдаваться другой IL в зависимости от того, переопределяет ли тип значения метод System.Object. Например, если тип значения V переопределяет метод Object.ToString(), выдается инструкция вызова 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()
Это называется боксом или автобоксом, CLR автоматически создает экземпляр соответствующего класса из типа значения, если вы вызываете какой-либо метод для него.