2012-09-20 28 views
6

He estado jugando con algunas declaraciones C# en LINQPad con el fin de comprender qué código de lenguaje intermedio se emite.El tipo dinámico C# hace que Console.WriteLine se resuelva con reflejo en IL

primera vez que trató el siguiente código:

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

y vio las siguientes seis líneas de IL emitidos:

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 

Lo cual, es en términos generales lo que espero, y es bastante una buena demostración de cómo los tipos anónimos son de solo lectura/inmutables, ya que no hay una propiedad set_Name.

Después probé las declaraciones:

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

lo que provoca una gran cantidad de IL ser emitida. No lo pegaré aquí, pero lo puedes encontrar en this pastebin.

Entiendo que hay bastante sobrecarga con respecto a la administración del tipo dinámico y ExpandoObject, pero no entiendo por qué parece que la llamada al System.Console.WriteLine se realiza en este caso a través de la reflexión interna.

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

En el primer segmento de código, después de la propiedad fue recuperado y almacenado, fue una declaración IL de una línea que invoca System.Console.WriteLine.

Entonces, ¿por qué se requiere todo esto adicional para la llamada con un tipo dynamic?

Respuesta

7

Como la variable es dynamic, no hay forma de saber, en tiempo de compilación, qué sobrecarga de WriteLine se debe invocar. No es hasta el tiempo de ejecución que conocemos el tipo real del objeto dynamic. Debido a la forma en que funciona el dynamic, es importante que no solo se trate como un object en tiempo de compilación; parte de la potencia es que está determinando la sobrecarga correcta en el tiempo de ejecución.

Si lanzas el objeto a algo que no sea dinámica (es decir string después de llamar ToString o simplemente volver a ExpandoObject) y luego pasarlo a WriteLine entonces usted debe ver que la llamada reflejo desaparece y lo ven de forma estática a determinar, en tiempo de compilación , la sobrecarga adecuada de WriteLine.

+0

En realidad, su truco 'ToString()' no funcionará (a menos que también agregue un molde explícito a 'cadena'). – svick

+0

@svick Estás en lo correcto, actualizado en consecuencia. – Servy

1

Lo que está sucediendo es que el compilador está creando su código de tal manera que puede ser "enlazado tarde". La vinculación tardía significa que en lugar de resolver los objetos durante la compilación, como ocurre con los tipos de datos y objetos tradicionales, el objeto se resuelve en tiempo de ejecución, mientras que el ensamblado se encuentra realmente en memoria y ejecutándose.

Si mirara su código en Reflector o dotPeek, vería que sus objetos dinámicos estaban decorados con un atributo [Dynamic]. Mientras su programa se ejecuta en la memoria, cuando se trata de un objeto que ha sido decorado con este atributo, la llamada a este objeto se canaliza a través de un Container dinámico (o como se llame a su objeto). Este Container se inicializa con el Binder responsable del enlace de tiempo de ejecución. Eso es lo que hace todo lo llamado a Microsoft.CSharp.RuntimeBinder. Este RuntimeBinder se usa más adelante para invocar propiedades o métodos o lo que sea dinámico.

Espero que esto aclare un poco las cosas. Estoy escribiendo en mi Android, por lo que la explicación podría ser menos que ideal. Lo limpiaré más tarde.

+0

No verá ningún atributo en este caso, porque las variables locales no pueden tener atributos. – svick

+0

@svick - No, pero las propiedades pueden tener atributos y en su ejemplo 'Name' es una propiedad – Icemanind

+0

Pero el tipo de esa propiedad es' string', no 'object' with' DynamicAttribute'. – svick

Cuestiones relacionadas