qui qualche programma di test in C#:Perché compilatore C# in alcuni casi emette newobj/stobj anziche 'chiamare esempio .ctor' per struct di inizializzazione
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);
}
}
e qui smontata versione compilata di metodo 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
Non capisco perché newobj/stobj è stato emesso invece di semplice chiamata .ctor? Per renderlo più misterioso, newobj + stobj ottimizzato da JIT-compilatore in modalità a 32 bit per una chiamata ctor, ma non lo fa in modalità a 64 bit ...
UPDATE:
Per chiarire la mia confusione, di seguito sono le mie aspettative.
value-type espressione dichiarazione come
Foo foo = new Foo(10)
è elaborato tramite
call instance void Foo::.ctor(int32)
value-type espressione dichiarazione come
Foo foo = default(Foo)
dovrebbe essere compilato tramite
initobj Foo
a mio variabile TEMP parere in caso di espressione di costruzione, o l'istanza di espressione di default dovrebbe essere considerato come variabile di destinazione, come questo non poteva seguire per ogni comportamento pericoloso
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
}
assegnazione espressione come
foo = new Foo(10); // foo declared somewhere before
è elaborato per qualcosa di simile:
.locals init (
...
valuetype Foo __temp,
...
)
...
ldloca __temp
ldc.i4 10
call instance void Foo::.ctor(int32)
ldloc __temp
stloc foo
...
questo il modo in cui ho capito che cosa dice C# specifica:
7.6.10.1 creazione oggetto espressioni
...
L'elaborazione in fase di esecuzione di un'espressione di creazione dell'oggetto della forma new T (A), in cui T è di tipo classe o struct-type e A è un argomento-elenco facoltativo, consiste dei seguenti passaggi:
...
Se T è una struttura di tipo:
un'istanza di tipo T viene creato allocando un temporaneo variabile locale.Dal momento che un costruttore di una struct tipo istanza è necessario assegnare sicuramente un valore per ciascun campo dell'istanza create, nessuna inizializzazione della variabile temporanea è necessaria.
Il costruttore di istanza è invocato secondo le regole del membro invocazione funzione (§7.5.4). Un riferimento all'istanza appena assegnata viene automaticamente passato al costruttore dell'istanza e l'istanza può essere richiamata all'interno di tale costruttore come questo.
Voglio fare l'accento su "l'assegnazione di una variabile temporanea locale". e nella mia comprensione l'istruzione newobj presuppone la creazione dell'oggetto su heap ...
La dipendenza dalla creazione dell'oggetto da come è stata usata mi ha reso in questo caso, come foo1 e foo2 mi sembrano identici.
possibile duplicato del [Differenza tra istanza chiamata vs esempio newobj in IL] (http://stackoverflow.com/questions/11966930/difference-between-call-instance-vs-newobj-instance-in -il) –
C l'uriosità è okay, ma questo richiede un significativo scavo. Scarica SSCLI20 e guardare il codice sorgente del compilatore C#, csharp/sscomp/ilgen.cpp, metodo ILGENREC :: genCall(). Qualcosa a che fare con il possibile aliasing, penso. –
@HansPassant: sei corretto; l'ottimizzatore C# sta saltando la copia elision perché è preoccupato del possibile aliasing. In questo particolare codice che è un'ipotesi eccessivamente prudente; in realtà non c'è alcun problema di aliasing qui. Ma piuttosto che fare che l'analisi, il compilatore C# rileva l'arbitro e si arrende sull'ottimizzazione elision presto. –