ClubEnsayos.com - Ensayos de Calidad, Tareas y Monografias
Buscar

C Sharp 2.0

ipala5 de Noviembre de 2013

7.101 Palabras (29 Páginas)359 Visitas

Página 1 de 29

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 extraídas de miCola a su tipo concreto, ¿qué ocurrirá si por error introduce un valor que no es ni string ni de un tipo convertible a string (por ejemplo, unint) y al extraerlo sigue solicitando su conversión a string? Pues que el compilador no se dará cuenta de nada y en tiempo de ejecución saltará una InvalidCastException.

Para resolver esto podría pensarse en derivar un tipo ColaString de Cola cuyos métodos públicos trabajasen directamente con cadenas de textos (Encolar(string valor) y string Desencolar()) Sin embargo, no es una solución fácil de reutilizar ya que para cualquier otro tipo de elementos (pe, una cola de ints) habría que derivar una nueva clase de Cola.

Otro problema de ambas soluciones es su bajo rendimiento, puesto que cada vez que se almacene un objeto de un tipo referencia en la cola habrá que convertir su referencia a una referencia a object y al extraerlo habrá que volverla a transformar en una referencia a string. ¡Y para los tipos valor todavía es peor!, en tanto que habrá que realizar boxing y unboxing, procesos que son mucho más lentos que las conversiones de referencias.

Si por el contrario se hubiese definido la cola utilizando genéricos tal y como sigue:

public class Cola<T>

{

T[] elementos;

public int NúmeroElementos;

public void Encolar(T valor)

{…}

public T Desencolar()

{…}

}

Entonces la extracción de objetos de la cola no requeriría de ningún tipo de conversión y sería tan cómoda y clara como sigue:

Cola<string> miCola = new Cola<string>();

miCola.Encolar("Esto es una prueba");

string valorDesencolado = miCola.Desencolar();

Si ahora por equivocación el programador solicitase almacenar un objeto cuyo tipo no fuese ni string ni convertible a él, obtendría un error al compilar informándole de ello y evitando que el fallo pueda llegar al entorno de ejecución. Además, el rendimiento del código es muy superior ya que no requerirá conversiones de referencias a/desde object. Si realiza pruebas podrá comprobar que la utilización de genéricos ofrece mejoras en el rendimiento entorno al 20% para los tipos referencia, ¡y al 200% para los tipos valor!

Sintaxis

El CLR de .NET 2.0 permite definir genéricamente tanto clases como estructuras, interfaces, delegados y métodos. Para ello basta con indicar tras el identificador de las mismas su lista de sus parámetros genéricos entre símbolos < y > separados por comas. Con ello, dentro de su definición (miembros de las clases, cuerpos de los métodos, etc.) se podrá usar libremente esos parámetros en cualquier sitio en que se espere un nombre de un tipo. La siguiente tabla muestra un ejemplo para cada tipo de construcción válida:

Ejemplo declaración Ejemplo

...

Descargar como (para miembros actualizados) txt (40 Kb)
Leer 28 páginas más »
Disponible sólo en Clubensayos.com