2012-09-20 21 views
6

Ho giocato con alcune affermazioni C# in LINQPad allo scopo di comprendere quale codice di lingua intermedio viene emesso.Il tipo dinamico C# fa sì che Console.WriteLine venga risolto con il riflesso in IL

ho provato il seguente codice:

var Container = new {Name = "James"}; 
Console.WriteLine(Container.Name); 

e vide le seguenti sei linee di IL emessi:

IL_0001: ldstr  "James" 
IL_0006: newobj  <>f__AnonymousType0<System.String>..ctor 
IL_000B: stloc.0  
IL_000C: ldloc.0  
IL_000D: callvirt <>f__AnonymousType0<System.String>.get_Name 
IL_0012: call  System.Console.WriteLine 

Il che, è sostanzialmente quello che mi aspetto, ed è piuttosto una bella dimostrazione di in che modo i tipi anonimi sono di sola lettura/immutabili, poiché non esiste una proprietà set_Name.

Poi ho provato le dichiarazioni:

dynamic Container = new System.Dynamic.ExpandoObject(); 
Container.Name = "James"; 
Console.WriteLine(Container.Name); 

che causa una quantità enorme di IL essere emessi. Non lo incollo qui, ma puoi trovarlo in this pastebin.

Capisco che c'è un po 'di overhead per quanto riguarda la gestione del tipo dinamico e ExpandoObject, ma non capisco perché sembra che la chiamata a System.Console.WriteLine sia in questo caso eseguita tramite riflessione interna.

IL_0072: ldstr  "WriteLine" 
.... 
IL_00BF: ldtoken  System.Console 

Nel primo segmento di codice, dopo che la proprietà è stato recuperato e conservato, è stata una IL dichiarazione di una linea che ha richiamato System.Console.WriteLine.

Quindi perché tutto questo extra è necessario per la chiamata con un tipo dynamic?

risposta

7

Poiché la variabile è dynamic non è possibile conoscere, in fase di compilazione, quale sovraccarico di WriteLine deve essere chiamato. Non è fino al runtime che conosciamo il tipo effettivo dell'oggetto dynamic. A causa del modo in cui funziona dynamic, è importante che non venga trattato come un object in fase di compilazione; parte del potere è che sta determinando il sovraccarico corretto in fase di esecuzione.

Se lanci l'oggetto a qualcosa di diverso dinamico (cioè string dopo aver chiamato ToString o semplicemente tornare a ExpandoObject) e poi passarlo a WriteLine allora si dovrebbe vedere che chiamata riflessione va via e vederlo determinare staticamente, al momento della compilazione , il corretto sovraccarico di .

+0

In realtà, il trucco 'ToString()' non funzionerà (a meno che non si aggiunga un cast esplicito a 'string'). – svick

+0

@svick Si è corretto, aggiornato di conseguenza. – Servy

1

Quello che sta succedendo è che il compilatore sta creando il codice in modo tale che possa essere "in ritardo". Il binding tardivo significa che piuttosto che risolvere gli oggetti durante la compilazione, come con i tipi di dati e gli oggetti tradizionali, l'oggetto viene risolto in fase di esecuzione, mentre l'assembly è effettivamente in memoria e in esecuzione.

Se si dovesse guardare il codice in Reflector o dotPeek, si vedrebbe che i propri oggetti dinamici sono stati decorati con un attributo [Dynamic]. Mentre il tuo programma è in esecuzione in memoria, quando si tratta di un oggetto che è stato decorato con questo attributo, la chiamata a questo oggetto viene convogliata attraverso un dinamico Container (o qualunque sia il nome dell'oggetto). Questo Container viene inizializzato con il Raccoglitore responsabile del bind di runtime. Questo è ciò che fa tutto il chiamato a Microsoft.CSharp.RuntimeBinder. Questo RuntimeBinder viene utilizzato in seguito per richiamare proprietà o metodi o qualsiasi cosa sia dinamica.

Spero che questo chiarisca un po 'le cose. Sto digitando sul mio androide, quindi la spiegazione potrebbe non essere l'ideale. Lo pulirò più tardi.

+0

In questo caso non vedresti alcun attributo, perché le variabili locali non possono avere attributi. – svick

+0

@svick - No, ma le proprietà possono avere attributi e nel suo esempio 'Name' è una proprietà – Icemanind

+0

Ma il tipo di quella proprietà è' stringa', non 'oggetto' con' DynamicAttribute'. – svick

Problemi correlati