2012-12-27 13 views
8

Eventuali duplicati:
C# “as” cast vs classic castChe cosa è un calco sotto il cofano

Voglio sapere cosa succede sotto il cofano del CLR .NET quando faccio qualcosa come

 
object myObj = "abc"; 
string myStr = (string)myObj; 

e come la seconda riga differisce da string myStr = myObj.ToString() o string myStr = myObj as string;

guardandomi intorno ho trovato risposte di tipo generico come "il compilatore inserisce il codice lì" ma non sono soddisfatto ... Sto cercando una profonda conoscenza della meccanica del cast ... Oh, il compilatore inserisce il codice? Fammi vedere! Il compilatore ottimizza il codice? Come? Quando?

Pls avvicinarsi il più possibile al metallo !!!

+0

Hai dato un'occhiata all'IL prodotto per un cast del genere? – Oded

+0

Immagino che un cast sia strettamente 'stringa myStr = myObj come stringa;' Forse la risposta può includere le differenze (per quanto riguarda CLR ...) – EtherDragon

+1

@EtherDragon - 'as' è più di un" try cast ". La sintassi dell'OP è sicuramente un cast. – Oded

risposta

3

È possibile utilizzare IL Dissasembler per vedere ciò che viene prodotto dal codice su un livello inferiore. Se hai installato Visual Studio sul tuo computer, dovresti riuscire a trovarlo semplicemente digitando "ildasm" nella casella di ricerca di Windows.

Ecco cosa l'IL del seguente codice si presenta come:

object myObj = "abc"; 
string myStr = (string)myObj;  
string tostring = myObj.ToString(); 

enter image description here

+2

beh sembra come se dovessi andare più in profondità ... "IL_0008: castclass [mscorlib] System.String" sembra un altro livello di astrazione ... – Leonardo

+0

@Leonardo 'castclass' è un codice operativo msil. Non va più in basso finché il jitter non lo converte in binario. – asawyer

+2

@ Leonardo è così profondo come sono disposto ad andare in questo genere di cose :). http://en.wikipedia.org/wiki/Common_Intermediate_Language c'è una spiegazione dei comandi di STLOC ecc. – Thousand

2

Un cast è in primo luogo un costrutto fase di compilazione. È il tuo modo di dire al compilatore: "Conosco meglio di te, questa istanza che pensi sia tale e un tale tipo è * in realtà * di questo altro tipo. Fai solo finta che sia davvero questo altro tipo e fammi usare tutto i metodi/proprietà/campi/ecc. di questo altro tipo

In fase di esecuzione modifiche molto piccole: praticamente l'unica cosa aggiunta è un controllo per assicurarsi che l'istanza sia realmente del tipo che stavi cercando di trasmettere a, e se non fosse che sarà un'eccezione e 'più o meno:..

if(myObj.GetType() != typeof(string)) 
    throw new ClassCastException(); 

per quanto riguarda ToString, è semplicemente un metodo che restituisce un string la sua definizione all'interno del string c molto probabilmente è solo il return this;. In ogni caso, tecnicamente non stai trasmettendo nulla, stai solo chiamando un metodo che ha ogni oggetto che restituisce una stringa, e quando quell'oggetto è un string sotto il cofano ti capita di riprendere quello stesso oggetto. La chiave è che il compilatore sa che il risultato della chiamata al metodo è sempre una stringa, quindi niente di speciale deve essere fatto.

1

È possibile utilizzare ILDASM, fornito con Visual Studio. È un disassemblatore IL.

Ecco il codice:

public void Foo() 
{ 
    object myObj = "abc"; 
    string myStr = (string)myObj; 
} 

Ecco cosa ho ottenuto:

.method public hidebysig instance void Foo() cil managed 
{ 
    // Code size  15 (0xf) 
    .maxstack 1 
    .locals init ([0] object myObj, 
      [1] string myStr) 
    IL_0000: nop 
    IL_0001: ldstr  "abc" 
    IL_0006: stloc.0 
    IL_0007: ldloc.0 
    IL_0008: castclass [mscorlib]System.String 
    IL_000d: stloc.1 
    IL_000e: ret 
} // end of method ShowWhatCastIs::Foo 

Con ToString():

public void Foo2() 
{ 
    object myObj = "abc"; 
    string myStr = myObj.ToString(); 
} 

L'IL è:

.method public hidebysig instance void Foo2() cil managed 
{ 
    // Code size  15 (0xf) 
    .maxstack 1 
    .locals init ([0] object myObj, 
      [1] string myStr) 
    IL_0000: nop 
    IL_0001: ldstr  "abc" 
    IL_0006: stloc.0 
    IL_0007: ldloc.0 
    IL_0008: callvirt instance string [mscorlib]System.Object::ToString() 
    IL_000d: stloc.1 
    IL_000e: ret 
} // end of method ShowWhatCastIs::Foo2 

Quando lanci la stringa "abc" è memorizzato nella posizione 0 allora posizione 0 viene caricato il valore è gettata con "calco [mscorlib] System.String", allora quel valore viene memorizzato nella locazione 1

Quando si chiama .ToString() chiama System.Object.ToString() sulla stringa virtualmente. ToString() è definito in System.String

Ecco l'ILDASM per System.String.ToString():

.method public hidebysig virtual instance string 
     ToString() cil managed 
{ 
    .custom instance void System.Runtime.TargetedPatchingOptOutAttribute::.ctor(string) = (01 00 3B 50 65 72 66 6F 72 6D 61 6E 63 65 20 63 // ..;Performance c 
                          72 69 74 69 63 61 6C 20 74 6F 20 69 6E 6C 69 6E // ritical to inlin 
                          65 20 61 63 72 6F 73 73 20 4E 47 65 6E 20 69 6D // e across NGen im 
                          61 67 65 20 62 6F 75 6E 64 61 72 69 65 73 00 00) // age boundaries.. 
    .custom instance void __DynamicallyInvokableAttribute::.ctor() = (01 00 00 00) 
    // Code size  2 (0x2) 
    .maxstack 8 
    IL_0000: ldarg.0 
    IL_0001: ret 
} // end of method String::ToString 

E 'appena si ritorna.

1

CLR consente di eseguire il cast di un oggetto sul tipo o su qualsiasi tipo di base. Un cast per il tipo di base è già considerato sicuro ed è implicito.

per es.

Oggetto s = new String();

Ma il CLR vuole di specificare cast esplicito in fase di lancio per un tipo derivato

String str = s; // Give error and needs you to explicitly cast it 
//To get it to work 
String str = (String)s; 

Ora qui quello che succede s non viene convertito in stringa, ma controllato se è di tipo String o meno. Se si trova che sia di tipo String, il cast è successo altrimenti viene generata una InvalidCastExcetion. più

due modi per caso stanno usando è e come operatori

è operatore: Questo restituisce true se il cast riesce altrimenti falso.

ad es.

Object o = new Object(); 
Boolean b1 = (o is Object); // b1 is true 
Boolean b2 = (o is DateTime); // b2 is false 

Quindi, prima di chiamare qualsiasi metodo, di solito si scrive codice come questo

if(o is DateTime) // Check this by observing the object 
{ 
    DateTime d = (DateTime)o; // Again cast the object to obtain a reference 
    // Perform operations 
} 

Questo è un po 'costoso, perché CLR getta due volte.

Per evitare questo, abbiamo l'operatore come.

come operatore: restituisce il riferimento al tipo di oggetto selezionato, altrimenti restituisce null.

ad es.:

DateTime d = o as DateTime; // Check the object to verify the case and returns reference to the object itself or null 

if(d != null) 
{ 
    // Perform the operations 
} 

Quindi, è possibile notare un lieve aumento delle prestazioni quando si utilizza come operatore.

Questo è tutto ciò che il CLR ha da offrire quando si tratta di tipi di casting.


Quando si tratta di codice:

oggetto str = "abc";

str.ToString() chiamerà il metodo ToString della classe System.object che è un metodo virtuale. Quando si chiama un metodo virtuale, CLR controlla effettivamente il tipo di oggetto indicato dal chiamante.

Qui str sta effettivamente puntando a un oggetto stringa. Quindi il compilatore genererà il codice per chiamare il metodo ToString della classe String che stamperà "abc" come output. Questo concetto è il polimorfismo in cui quando si chiama un metodo virtuale di qualsiasi tipo, il tipo di oggetto effettivo viene prima ottenuto dal CLR e quindi il metodo appropiato viene chiamato sul tipo corretto dell'oggetto come tipo String nel caso.