здесь некоторая тестовая программа в С#:
using System;
struct Foo {
int x;
public Foo(int x) {
this.x = x;
}
public override string ToString() {
return x.ToString();
}
}
class Program {
static void PrintFoo(ref Foo foo) {
Console.WriteLine(foo);
}
static void Main(string[] args) {
Foo foo1 = new Foo(10);
Foo foo2 = new Foo(20);
Console.WriteLine(foo1);
PrintFoo(ref foo2);
}
}
и здесь дизассемблированная скомпилированная версия метода Main:
.method private hidebysig static void Main (string[] args) cil managed {
// Method begins at RVA 0x2078
// Code size 42 (0x2a)
.maxstack 2
.entrypoint
.locals init (
[0] valuetype Foo foo1,
[1] valuetype Foo foo2
)
IL_0000: ldloca.s foo1
IL_0002: ldc.i4.s 10
IL_0004: call instance void Foo::.ctor(int32)
IL_0009: ldloca.s foo2
IL_000b: ldc.i4.s 20
IL_000d: newobj instance void Foo::.ctor(int32)
IL_0012: stobj Foo
IL_0017: ldloc.0
IL_0018: box Foo
IL_001d: call void [mscorlib]System.Console::WriteLine(object)
IL_0022: ldloca.s foo2
IL_0024: call void Program::PrintFoo(valuetype Foo&)
IL_0029: ret
} // end of method Program::Main
Я не понимаю, почему newobj/stobj был издан вместо простого вызова .ctor? Чтобы сделать его более загадочным, newobj + stobj оптимизирован jit-компилятором в 32-битном режиме на один вызов ctor, но он не находится в режиме 64 бит...
UPDATE:
Чтобы пояснить мое замешательство, ниже мое ожидание.
выражение типа значения типа типа
Foo foo = new Foo(10)
следует скомпилировать с помощью
call instance void Foo::.ctor(int32)
выражение типа значения типа типа
Foo foo = default(Foo)
следует скомпилировать с помощью
initobj Foo
на мой взгляд, временная переменная в случае конструктивного выражения или экземпляр выражения по умолчанию следует рассматривать как целевую переменную, так как это не может следовать за любым опасным поведением
try{
//foo invisible here
...
Foo foo = new Foo(10);
//we never get here, if something goes wrong
}catch(...){
//foo invisible here
}finally{
//foo invisible here
}
выражение присваивания типа
foo = new Foo(10); // foo declared somewhere before
должен быть скомпилирован примерно так:
.locals init (
...
valuetype Foo __temp,
...
)
...
ldloca __temp
ldc.i4 10
call instance void Foo::.ctor(int32)
ldloc __temp
stloc foo
...
так я понимаю, что говорит спецификация С#:
7.6.10.1 Выражения создания объектов
...
Обработка времени создания объекта-выражения формы new T (A), где T является классом или структурным типом, а A является необязательным списком аргументов, состоит из следующих шагов:
...
Если T является структурным типом:
Экземпляр типа T создается путем выделения временной переменной local. Поскольку конструктор экземпляра struct-type требуется для того, чтобы определенно присвоить значение каждому полю создаваемого экземпляра, не требуется инициализация временной переменной.
Конструктор экземпляра вызывается в соответствии с правилами вызова функции-члена (§7.5.4). Ссылка на вновь выделенный экземпляр автоматически передается в конструктор экземпляра, и экземпляр может быть доступен изнутри этого конструктора, как это.
Я хочу сделать упор на "выделение временной локальной переменной". и в моем понимании newobj инструкция предполагает создание объекта на куче...
Зависимость создания объекта от того, как он используется, заставляет меня в этом случае, так как foo1 и foo2 выглядят одинаково для меня.