Inferencia de Tipos: Cómo funciona ?

Si hay algo que veo que cuesta que los desarrolladores entiendan o mejor dicho confíen es en la inferencia de tipos que presenta  .net  Framework 3.5. Muchos creen que al usar la palabra var, están  resucitando al costoso tipo Variant de Visual Basic 6.0 o creen que están declarando una variable del tipo Object y que en tiempo de ejecución se resuelve el tipo al que pertenece dicha variable como los hacen los lenguajes dinámicos.


Afortunadamente no es así, la resolución del tipo al que pertenece una variable se resuelve en tiempo de compilación no en tiempo de ejecución.  Vamos a verlo con un ejemplo:



La inferencia de tipos es un proceso por el cual el compilador determina el tipo de una variable local que ha sido declarada sin una declaración explícita de su tipo. El tipo es inferido a partir del valor inicial provisto a la variable. A continuación he declarado una variable llamada “inferredType”, cuyo tipo será resuelto por el compilador C# teniendo en cuenta por supuesto el contenido de la variable. Lo cual revela que para que el algoritmo de inferencia de tipos funcione es necesaria una entrada, que es el contenido de la variable. Si no inicializamos la variable a inferir  tendremos un error de compilación.




La ejecución nos revela que el compilador ha inferido que el contenido de “inferredType” es del tipo System.Int32.



Este resultado sería el mismo que obtendríamos si hubiéramos escrito el siguiente código:



La mejor forma de verificar esto es analizando  el IL que generan ambas implementaciones tanto la inferida como la explícita. Con la ayuda de Reflector el código fuente en C# que se obtiene de analizar el IL de ambos ejecutables es exactamente el mismo.



Ahora bien, analicemos esta situación de error:



Particularmente lo que sucede en este caso, es que el compilador ya infirió que el tipo de la variable “inferredType” es System.Int32. Al asignarle contenido de tipo System.String tenemos un error de conversión de tipos. Me parece importante destacar esta situación ya que conozco a mucha gente que pretende que el compilador analice todas las posibles expresiones en las que interviene la variable y posteriormente generalice al tipo más adecuado para la misma. Pues les tengo malas noticias, el algoritmo de inferencia funciona a partir de la primera evaluación eventual  de una expresión, y esto está bien que sea así ya que la función del compilador es resolver el tipo mediante la reducción de expresiones al tipo atómico más implícito que le permita compilar. En el caso de este ejemplo la expresión analizar es una constante numérica. Si se analizaran todas las expresiones en las que interviene una variable para posteriormente generalizar al mejor tipo, el compilador necesitaría  una instancia privada de SQL Server Analisys Services y tardaría bastante tiempo en generar un ejecutable, lo cual no es la idea.
Ahora si queremos ver el mismo ejemplo con una cadena de texto , el código es el siguiente:


 


Al ejecutarlo obtenemos:



Y cuando analizamos el IL con reflector, conseguimos:



La inferencia en los tipos por valor generaliza al tipo más implícito y optimizado del .net Framework. Como el Framework optimiza la performance para tipos enteros de 32-bit(System.Int32 y System.UInt32) un valor de 0,10 ó 100 que perfectamente podría inferirse como System.Byte se infiere como System.Int32. Incluso se recomienda usar los tipos enteros para contadores(aunque contemos de 0 a 10) y variables enteras de acceso frecuentes, ya que la performance en tiempo de ejecución del tipo entero es preferible al storage en RAM que ahorramos si declaramos variables como System.SByte, System.Byte y System.Int16. De la misma manera, con valores de  punto flotante si declaramos una variable con un valor de 3.14 será inferida al tipo System.Double y no como System.Single(float) que perfectamente la puede contener. La razón es que las operaciones con System.Double son optimizadas por hardware. Sólo se infiere a un tipo no optimizado por el Framework(como System.Int64 ó System.Decimal) si el valor de la variable está fuera del rango de los tipos optimizados.
Si por ejemplo queremos que se infiera el valor 3.14 como float en vez de double, debemos proporcionar cierta evidencia que ayude al compilador a inferirlo como float.


var inferredType= (float)3.14; // casting explícito

var inferredType = 3.14f; // notación sufijo

Entonces, resumiento:




  • La inferencia de tipos no es una solución automágica, el compilador no es psíquico ni el tipo de la variable se resuelve utilizando mecanismos de código dinámico, que afecten la performance en tiempo de ejecución.


  • La inferencia de tipos de resuelve en tiempo de compilación, por lo tanto existe un costo en tiempo de compilación, ese costo  es el tiempo que tarda el algoritmo de inferencia en sintetizar una expresión y resolver el tipo de una variable. 


  • La inferencia de tipos tanto de valor como de referencia es para variables locales de métodos. No se aplica para variables de clase, propiedades, parámetros ni valores de retorno.


  • La inferencia de tipos no es nada más que azúcar sintáctica. Es decir una forma de endulzar y agilizar la manera de declarar variables locales. Realmente se aprecia y se valora  cuando se usa para el desarrollo de pruebas unitarias.


  • El tipo generado por el proceso de inferencia NO es un VARIANT.  Visual Basic  6.0 ha sido discontinuado y es considerado tecnología legacy así que actualícense a Visual Basic.net

Fernik

Estrenando geeks.ms y blog en español

Hola,

Mi nombre es Fernando Hualpa. Soy más conocido como Fernik en Internet y blogs. Antes que nada quiero agradecer a la gente de Plain Concepts por haberme ofrecido un blog en geeks.ms, realmente lo valoro mucho. Mi antiguo blog está en http://fernik.spaces.live.com, generalmente posteaba en inglés, lo cual pienso seguir haciendo  ya que me gusta mucho producir en inglés pero me parece buena idea empezar a generar contenido en español ya que es mi idioma nativo.

Actualmente me desempeño como Microsoft Student Partner en Argentina y como desarrollador en el sector privado, hace 7 años que soy beta tester externo de Microsoft especialmente de Visual Studio, Windows en sus versiones cliente y servidor y SQL Server. Esta actividad me permitió valorar y conocer el proceso de Testing y convertirme en un mejor desarrollador.

Esencialmente creo que es suficiente para empezar, así que sólo me queda decir que si desean contactarse por Live Messenger lo pueden hacer a través de la barra lateral cuando el ícono de presencia esté en verde.

Casi me olvidaba, tengo un par de invitaciones para la beta de Live Mesh, si quieren que los invite por favor comenten esta entrada inicando el email que usan como cuenta passport o Live ID. 

Fernik