Приводит ли вызов метода для типа значения к упаковке в .NET?
-
22-07-2019 - |
Вопрос
Я только что участвовал в вопросе о переполнении стека Все ли в .NET является объектом?.
И один автор (в комментариях к принятому ответу), похоже, думал, что выполнение вызова метода для типа значения приводит к боксу.Он указал мне на Упаковка и распаковка (Руководство по программированию на C#) что не совсем точно определяет описываемый нами вариант использования.
Я не из тех, кто доверяет одному источнику, поэтому я просто хотел получить дополнительные отзывы по этому вопросу.Моя интуиция подсказывает, что бокса нет, но моя интуиция — отстой.:D
Чтобы уточнить:
Пример, который я использовал, был:
int x = 5;
string s = x.ToString(); // Boxing??
Бокс делает нет происходит, если рассматриваемая структура переопределяет метод, унаследованный от объекта, как указано в принятом ответе.
Однако, если структура не переопределяет метод, «ограничение» КИЛ команда выполняется до вызоваvirt.Согласно документации, OpCodes.Constrained Поле, это приводит к боксу:
Если ThisType является типом значения, а ThitType не реализует метод, то PTR находится в образе, в штучной упаковке и передается как «этот» указатель на инструкцию метода CallVirt.
Решение
Вот IL для вашего кода:
L_0001: ldc.i4.5 // get a 5 on the stack
L_0002: stloc.0 // store into x
L_0003: ldloca.s x // get the address of x on the stack
L_0005: call instance string [mscorlib]System.Int32::ToString() // ToString
L_000a: stloc.1 // store in s
Таким образом, ответ в этом случае - нет.
Другие советы
В случае, если вы дали ответ, нет, как указал Плинтус.
Однако это произойдет, если вы вызовете метод через указатель интерфейса.
Рассмотрим код:
interface IZot
{
int F();
}
struct Zot : IZot
{
public int F()
{
return 123;
}
}
Затем
Zot z = new Zot();
z.F();
Делает нет Результат в боксе:
.locals init (
[0] valuetype ConsoleApplication1.Zot z)
L_0000: nop
L_0001: ldloca.s z
L_0003: initobj ConsoleApplication1.Zot
L_0009: ldloca.s z
L_000b: call instance int32 ConsoleApplication1.Zot::F()
L_0010: pop
L_0011: ret
Однако это делает:
IZot z = new Zot();
z.F();
.locals init (
[0] class ConsoleApplication1.IZot z,
[1] valuetype ConsoleApplication1.Zot CS$0$0000)
L_0000: nop
L_0001: ldloca.s CS$0$0000
L_0003: initobj ConsoleApplication1.Zot
L_0009: ldloc.1
L_000a: box ConsoleApplication1.Zot
L_000f: stloc.0
L_0010: ldloc.0
L_0011: callvirt instance int32 ConsoleApplication1.IZot::F()
L_0016: pop
«Я считаю, что вызов ToString, Equals и Gethashcode приводит к боксу, если структура не переопределяет методы».
Я проверил ToString для вас.Int32 переопределяет ToString, поэтому я создал структуру, которая этого не делает.я использовал .NET-отражатель чтобы гарантировать, что структура не каким-то волшебным образом переопределяет ToString(), и это не так.
Итак, код был таким:
using System;
namespace ConsoleApplication29
{
class Program
{
static void Main(string[] args)
{
MyStruct ms = new MyStruct(5);
string s = ms.ToString();
Console.WriteLine(s);
}
}
struct MyStruct
{
private int m_SomeInt;
public MyStruct(int someInt)
{
m_SomeInt = someInt;
}
public int SomeInt
{
get
{
return m_SomeInt;
}
}
}
}
И MSIL (через ИЛДАСМ) для метода Main это:
IL_0000: ldloca.s ms
IL_0002: ldc.i4.5
IL_0003: call instance void ConsoleApplication29.MyStruct::.ctor(int32)
IL_0008: ldloca.s ms
IL_000a: constrained. ConsoleApplication29.MyStruct
IL_0010: callvirt instance string [mscorlib]System.Object::ToString()
IL_0015: stloc.1
IL_0016: ldloc.1
IL_0017: call void [mscorlib]System.Console::WriteLine(string)
IL_001c: ret
Теперь, несмотря на отсутствие боксерского вызова, если вы отметите документация про ограниченный + вызов вирт, вы обнаружите, что там говорится, что бокс ДЕЙСТВИТЕЛЬНО имеет место.ооо
Цитировать:
Если thisType является типом значения и thisType не реализует метод Затем ptr разыменовывается, упаковывается и передается в качестве указателя 'this' на объект Инструкция по методу callvirt.
Я полагаю, что вызов ToString, Equals и Gethashcode приводит к созданию бокса, если структура не переопределяет методы.