Tipos anónimos: Quién se hace cargo ?

Un tipo anónimo es una clase cuyo nombre es generado por el compilador y que hereda directamente de System.Object. Los miembros de un tipo anónimo son propiedades que son inferidas del inicializador de objeto que crea instancias del tipo en cuestión.
Es posible crear una clase anónima mediante la palabra reservada new y un par de llaves que definen las propiedades públicas y los valores que se desea que la clase contenga. Por ejemplo:

anonymousObject = new { Nombre = «Pedro», Edad = 42 };

La clase anónima anterior contiene 2 propiedades públicas:

  • Nombre (inicializada con la cadena “Juan”)
  • Edad (inicializada con el entero 42)

El compilador infiere los tipos de las propiedades a partir de los valores que se especifican para inicializarlas.

Cuando se define una clase anónima, el compilador genera su propio nombre para la clase, pero no lo revela ni lo hace explícito. Sin embargo en tiempo de ejecución podemos obtener el nombre de la clase generada.

Y aquí surge una paradoja: Si no se conoce el nombre de la clase, cómo es posible crear un objeto(anonymousObject) a partir de esa clase y asignar una instancia(new { Nombre = «Pedro», Edad = 42 }) al mismo ?
La respuesta está en la inferencia de tipos, si se declara a anonymousObject como una variable implícitamente tipada con la palabra reservada var, el compilador creará una variable del mismo tipo que la expresión utilizada para inicializarla. En este caso el tipo de la expresión es el nombre que el compilador genera para la clase anónima. Para que quede claro observemos el código que genera Reflector en relación a la clase anónima generada y a la inferencia de tipos.

La clase anónima que el compilador genera es una clase genérica, si bien Reflector nos muestra una visión un poco confusa, lo correcto sería que hubiese interpretado a la palabra var así:

<>f__AnonymousType0<string, int> anonymousObject = new <>f__AnonymousType0<string, int>(«Pedro», 42);

La clase anónima generada por el compilador es una plantilla, la cual el mismo compilador parametriza, con los tipos que infiere del inicializador de objetos. El nombre que el compilador asignó a la clase anónima es <>f__AnonymousType0 y su definición es la siguiente:

También es posible crear otras instancias y reutilizar la misma clase anónima pero con diferentes valores en sus propiedades.

Al ejecutar el programa, ambas instancias son del mismo tipo:

Reflector también lo confirma, ya que ambos objetos han sido instanciados con los mismos parámetros en la clase genérica.

El compilador C# utiliza los mismos nombres, tipos, número y orden de las propiedades para determinar si 2 instancias de una clase anónima pertenecen al mismo tipo. En este caso como ambas variables tienen el mismo número de propiedades, con el mismo nombre, tipo inferido y orden entonces ambas variables son instancias de la misma clase anónima con los mismos parámetros genéricos.

En síntesis las clases anónimas si tienen un nombre,  ya que el nombre es fundamental en la programación orientada a objetos para identificar y clasificar. Pero ese nombre no lo controla ni lo elige el desarrollador sino el compilador.  Si bien tienen la ventaja de permitirnos crear objetos de una forma flexible(lo cual se aprecia cuando usamos expresiones de consulta) poseen muchas restricciones en su contenido, ya que sólo pueden contener propiedades públicas inicializadas y de sólo lectura.

Ahora les dejo algo para pensar y comentar: 

var anonymousObject = new { Nombre = «Pedro», Edad = 42 };

var anonymousObject2 = new { Nombre = «Juan», Edad = 42f };

if (anonymousObject.GetType() == anonymousObject2.GetType())

Console.WriteLine(«Tipos iguales»);

else

Console.WriteLine(«Tipos distintos»);

 

Teneniendo en cuenta que la única diferencia entre ambos objetos es el tipo de la propiedad Edad, en uno es entero y en otro es flotante:

  • Cuántas clases anónimas generaría el compilador ?
  • Si el compilador genera 2 clases, entonces ambos tipos objetos siempre pertenecerán a tipos diferentes. Pero si genera 1 clase, en qué casos los tipos serían diferentes ?

Fernik