C Sharp 2.0
Enviado por ipala • 5 de Noviembre de 2013 • 7.101 Palabras (29 Páginas) • 332 Visitas
Novedades de C# 2.0
Introducción
El 24 de Octubre de 2003 Microsoft hizo público el primer borrador de lo que sería la versión 2.0 del lenguaje C#, incluida en la nueva versión del .NET Framework conocida con el nombre clave Whidbey. En ella se introducía una importante novedad en el CLR consistente en proporcionar soporte para tipos genéricos que se pudiesen usar como plantillas en base a la que definir otros tipos. Esto lógicamente implicaba que a los lenguajes .NET de Microsoft en primer lugar, y presumiblemente el resto después, se les hiciesen también modificaciones orientadas a aprovechar esta nueva funcionalidad.
En este tema se explican las novedades para ello incluidas en la versión 2.0 de C#, así como otras novedades no directamente relacionadas con los genéricos que también incorpora: los iteradores para facilitar la implementación de las interfaces IEnumerablee IEnumerator, los métodos anónimos y otros mecanismos destinados a facilitar el trabajo con los delegados, la capacidad de dividir las definiciones de las clases entre varios ficheros a través de clases parciales, la posibilidad de asignar null a los tipos valor a través de los nuevos tipos valor anulables, etc.
En principio, las modificaciones introducidas en C# se han diseñado con la idea de mantener el máximo nivel de compatibilidadcon códigos escritos para las anteriores versiones del lenguaje –versiones 1.X-. Por ello, las nuevas palabras con significado especial introducidas (where, yield, etc.) no se han clasificado como reservadas, de modo que seguirán siendo válidos los identificadores que se hubiesen declarados con sus nombres. Sólo se han introducido unas mínimas incompatibilidades relacionadas con la sintaxis de los genéricos que se describen en el epígrafe Ambigüedades del tema.
Genéricos
Concepto de genéricos
C# 2.0 permite especificar los tipos utilizados en las definiciones de otros tipos de datos y de métodos de forma parametrizada, de manera que en vez de indicarse exactamente cuáles son se coloque en su lugar un parámetro –parámetro tipo- que se concretará en el momento en que se vayan a usar (al crear un objeto de la clase, llamar al método,…) A estas definiciones se les llama genéricos, y un ejemplo de una de ellas es el siguiente:
public class A<T>
{
T valor;
public void EstablecerValor(T valor)
{
this.valor = valor;
}
}
En esta clase no se han concretando ni el tipo del campo privado valor ni el del único parámetro del método EstablecerValor() En su lugar se le especificado un parámetro tipo T que se concretará al utilizar la clase. Por ejemplo, al crear un objeto suyo:
A<int> obj = new A<int>();
Esto crearía un objeto de la clase genérica A con el parámetro tipo T concretizado con el argumento tipo int. La primera vez que el CLR encuentre esta concretización de T a int realizará un proceso de expansión o instanciación del genérico consistente en generar una nueva clase con el resultado de sustituir en la definición genérica toda aparición de los parámetros tipos por los argumentos tipo. Para el ejemplo anterior esta clase sería:
public class A<int>
{
int valor;
public void EstablecerValor(int valor)
{
this.valor = valor;
}
}
A los tipos con parámetros tipo, como A<T>, se les llama tipos genéricos cerrados; a los generados al concretárseles algún parámetro tipo se le llama tipos construidos; y a los generados al concretárseles todos tipos genéricos abiertos. La relación establecida entre ellos es similar a la establecida entre las clases normales y los objetos: al igual que clases sirven de plantillas en base a las que crear objetos, los tipos genéricos cerrados actúan como plantillas en base a las que crear tipos genéricos abiertos. Por eso, en el C++ tradicional se llamaba plantillas a las construcciones equivalentes a los genéricos.
La expansión la hace el CLR en tiempo de ejecución, a diferencia de lo que sucede en otros entornos (pe, C++) en los que se realiza al compilar. Esto tiene varias ventajas:
• Ensamblados más pequeños: Como sólo almacenan el tipo genérico cerrado, que el CLR ya expandirá en tiempo de ejecución, su tamaño es más pequeño y se evita el problema del excesivo inflado del código binario generado (code bloat)
Además, para evitar el inflado de la memoria consumida, el CLR reutiliza gran parte del MSIL generado para la primera expansión de un genérico por un tipo referencia en las siguientes expansiones del mismo por otros tipos referencia, ya que todas las referencias son al fin y al cabo punteros que en memoria se representan igual.
• Metadatos ricos: Al almacenarse los tipos genéricos cerrados en los ensamblados, se podrán consultar mediante reflexión y ser aprovechados por herramientas como el IntelliSense de Visual Studio.NET para proporcionar ayuda sobre su estructura.
• Implementación fácil: Como es el propio CLR quien realiza gran parte del trabajo necesario para dar soporte a los genéricos, la inclusión de los mismos en cualquiera de los lenguajes .NET se simplifica considerablemente.
Usos de los genéricos
Los genéricos no son una novedad introducida por C# en el mundo de la programación, sino que otros lenguajes como Ada, Eiffel o C++ (plantillas) ya las incluyen desde hace tiempo. Su principal utilidad es, como su propio nombre indica, facilitar la creación de código genérico que pueda trabajar con datos de cualquier tipo. Esto es especialmente útil para crear tipos que actúen como colecciones (pilas, colas, listas, etc.), cosa que C# 1.X sólo permitía crear definiéndolos en base a la clase base común object. Por ejemplo, una cola que admitiese objetos de cualquier tipo había que declararla como sigue:
public class Cola
{
object[] elementos;
public int NúmeroElementos;
public void Encolar(object valor);
{…}
public object Desencolar()
{…}
}
El primer problema de esta solución es lo incómoda y proclive a errores que resulta su utilización, pues a la hora de extraer valores de la cola habrá que convertirlos a su tipo real si se quieren aprovechar sus miembros específicos. Es decir:
Cola miCola = new Cola();
miCola.Encolar("Esto es una prueba");
string valorDesencolado = (string) miCola.Desencolar();
Aparte de que el programador tenga que escribir (string) cada vez que quiera convertir alguna de las cadenas
...