domingo, 1 de julio de 2007

¿Cómo funciona un tipo anónimo?

En un post anterior vimos que el resultado de una expresión LINQ puede ser un tipo anónimo (casi siempre lo es), ahora nos preguntamos si esto no es una especie de variant de VB6 disfrazado y tiraría por tierra toda la promocionada comprobación de tipos de C#, esto no es verdad ya que un tipo anónimo si bien no se define explícitamente existe y por lo tanto no podemos decir que es una cosa y en la siguiente línea decir que es otra alegremente sin que el compilador diga nada, vamos a ver por qué. var persona = new {Nombre="Juan",Apellido="Perez", Edad=18 }; ObjectDumper.Write(persona); Este sencillo código podría ser el resultado de una expresión Lambda que daría lo mismo, el echo es que el resultado es un tipo anónimo, en ningún lado yo definí que existe un tipo que tiene tres atributos sin embargo el object dumper no miente (ya que utiliza reflection para determinar los atributos del tipo que deseamos imprimir) y es más, no podemos poner una línea a continuación diciendo persona="tal cosa" porque el compilador no nos deja, nos dice algo del estilo "no se pude converitir el tipo anónimo a string..." bueno, veamos por qué. Echamos mano del Reflector para variar y descompilamos el código generado. private void Test() { var <>g__initLocal0 = new <>f__AnonymousType0(); <>g__initLocal0.Nombre = "Juan"; <>g__initLocal0.Apellido = "Perez"; <>g__initLocal0.Edad = 0x12; var persona = <>g__initLocal0; ObjectDumper.Write(persona); } Si observamos un poco vemos lo siguiente "<>f__AnonymousType0()" y es justamte nuestro tipo anónimo, es más a continuación vemos como se asignan los valores a las propiedades que nosotros definimos, pero ¿qué pasó?, pasó que el compilador creo que tipo por nosotros, si hacemos click sobre la definición Reflector nos muestra la implementación. [CompilerGenerated, DebuggerDisplay(@"\{ Nombre = {Nombre}, Apellido = {Apellido}, Edad = {Edad} }", Type="")] internal sealed class <>f__AnonymousType0<<>j__AnonymousTypeTypeParameter1, <>j__AnonymousTypeTypeParameter2, <>j__AnonymousTypeTypeParameter3> { // Fields [DebuggerBrowsable(DebuggerBrowsableState.Never)] private <>j__AnonymousTypeTypeParameter1 <>i__AnonymousTypeField4; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private <>j__AnonymousTypeTypeParameter2 <>i__AnonymousTypeField5; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private <>j__AnonymousTypeTypeParameter3 <>i__AnonymousTypeField6; // Methods [DebuggerHidden] public override bool Equals(object value) { var type = value as <>f__AnonymousType0<<>j__AnonymousTypeTypeParameter1, <>j__AnonymousTypeTypeParameter2, <>j__AnonymousTypeTypeParameter3>; return ((((type != null) && object.Equals(this.<>i__AnonymousTypeField4, type.<>i__AnonymousTypeField4)) && object.Equals(this.<>i__AnonymousTypeField5, type.<>i__AnonymousTypeField5)) && object.Equals(this.<>i__AnonymousTypeField6, type.<>i__AnonymousTypeField6)); } [DebuggerHidden] public override int GetHashCode() { int num = -1393883635; num = (-1521134295 * num) + ((this.<>i__AnonymousTypeField4 != null) ? this.<>i__AnonymousTypeField4.GetHashCode() : 0); num = (-1521134295 * num) + ((this.<>i__AnonymousTypeField5 != null) ? this.<>i__AnonymousTypeField5.GetHashCode() : 0); return ((-1521134295 * num) + ((this.<>i__AnonymousTypeField6 != null) ? this.<>i__AnonymousTypeField6.GetHashCode() : 0)); } [DebuggerHidden] public override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append("{ Nombre = "); builder.Append(this.<>i__AnonymousTypeField4); builder.Append(", Apellido = "); builder.Append(this.<>i__AnonymousTypeField5); builder.Append(", Edad = "); builder.Append(this.<>i__AnonymousTypeField6); builder.Append(" }"); return builder.ToString(); } // Properties public <>j__AnonymousTypeTypeParameter2 Apellido { get { return this.<>i__AnonymousTypeField5; } set { this.<>i__AnonymousTypeField5 = value; } } public <>j__AnonymousTypeTypeParameter3 Edad { get { return this.<>i__AnonymousTypeField6; } set { this.<>i__AnonymousTypeField6 = value; } } public <>j__AnonymousTypeTypeParameter1 Nombre { get { return this.<>i__AnonymousTypeField4; } set { this.<>i__AnonymousTypeField4 = value; } } } Como vemos, están los tres atributos que definimos y algunas cosas más, como una sobrecarga del método ToString(), el Equals(), GetHashCode() y un poco de información para el debugger, pero en definitiva es un tipo contante y sonante y es por eso que no podemos hacer lo que querramos con él, simplemente no hace falta definirlo pero existe y el compilador será tan estricto en la comprobación de su uso con con cualquier otro tipo. Ahora me voy a dormir porque es muy tarde, nos leemos.

2 comentarios:

Tirana Tirani dijo...

nice blog and err very complicated programming there. why dont u sign up for google adsense for extra income.

come here and click the adsense link from my site =D

http://anime-drama.blogspot.com/

Leonardo Micheloni dijo...

Thank you, I do not seek to do money with my blog. You have my click.