jueves, 26 de diciembre de 2013

Conversión y Promoción de Referencias en C#

Tabla de Contenido

0. Introducción
1. Upcasting y Downcasting
2. Operadores as e is
Conclusiones
Glosario
Literatura & Literatura

0. Introducción

Es el momento que nos adentremos un poco más en el proceso de conversión y promoción de tipos en lenguaje de programación C#. Ampliaré el concepto de conversión explícita e implícita, también introduciré el uso de algunas construcciones de C# como as e is para llevar a cabo este proceso que seguramente resulte más obvia en ciertos escenarios, mientras que en otros prefiramos usar las construcciones naturales (ya verán a qué me refiero con esta expresión). Inclusive dedicaré tiempo para hablar de la importancia de este concepto en programación orientada a objetos (poliformismo,  precisamente).

1. Upcasting y Downcasting

[Nota: Empiezo por hacer una aclaración acerca del uso de los términos upcasting y downcasting, dado que he preferido usar los términos en su idioma original. Sin embargo, éstos se refieren a la dirección de conversión: el primero (upcasting) hace referencia a convertir un subtipo a un supertipo (por supuesto en la misma jerarquía de herencia, a excepción de object)], mientras que downcasting, es el proceso inverso, es decir, la conversión sucede desde un supertivo a un subtipo].

Para realizar ejemplos de demostración en las siguientes secciones, trabajaremos con la siguiente jerarquía de herencia.
Jerarquía de herencia Figura
Figura 1. Relación de herencia para el tipo Figura.

1.1 Upcasting: Desde Tipo Base a Tipo Derivado

A través del proceso de upcasting podemos crear una referencia de un supertivo apartir de una referencia de cualquier subtipo. Un ejemplo evidente de este proceso ocurre cuando utilizamos object para mantener la referencia a un objeto de cualquier subtipo de la jerarquía inherente a los demás tipos de la biblioteca de clases del Framework o cualquier tipo personalizado, es el siguiente:

string cadena = "Esta es una cadena de texto de ejemplo.";
Object obj = cadena; // Upcasting

En primer lugar, hemos creado una referencia -cadena- de tipo string (desde luego hasta aquí nada nuevo); pasamos a la siguiente línea donde se produce el proceso de upcasting y creamos una referencia object -obj- que apunta a la mismo objeto que cadena.

Vayamos un poco más a fondo con el reuso de la jerarquía que aparece en la Figura 1. Si tenemos las siguientes líneas de código:

Circulo objCirculo = new Circulo();
Figura figura = circulo; // upcasting

Nuevamente, en la segunda línea el proceso de upcasting nos permite referenciar a un objeto a Circulo desde su clase base (o sea, Figura). A través de [1] nos aclaran que ambas referencias (los identificadores) objCirculo y figura hacen referencia al mismo objeto. Sin embargo, desde figura se tiene una vista más restrictiva de tal objeto. Asúmamos que la clase Circulo contiene el método CalcularCircunferencia cuando invoquemos este método sobre objCirculo el compilador generará errores:

figura.Dibujar(); // Invocación polimórfica de Dibujar
figura.CalcularCircunferencia(); // Error: CalcularCircunferencia undefined

El error que se produce en la última línea corresponde de manera obvia con la llamada a un miembro ajeno al tipo de objeto Figura -CalcularCircunferencia- (esto independiente de que apunte a un tipo de la clase Circulo).

[Nota: En el proceso de upcasting la asignación de un subtipo a supertipo no requiere de operadores adicionales. Sólo obsérvese los ejemplos de código fuente C# en donde ocurre esto de forma implícita.]

1.1.1 Conversiones de referencias implícitas

En la Figura 2 [7] se presentan la lista de conversiones permitidas de manera implícita:
Figura 2. Conversiones de referencias implícitas [7].

1.2 Downcasting: Desde Tipo Derivado a Tipo Base

De acuerdo con [1]: el proceso de downcasting ocurre con la creación de una referencia de un tipo de dato heredado desde su clase una superclase en la misma jerarquía de herencia de tipos. Es decir, que ocurre el proceso inverso a upcasting. Veamos con código fuente de C#:

Circulo objCirculo = new Circulo();
Figura objFigura = objCirculo;     // Upcast
Circulo objCirculo2 = (Circulo) objFigura; // Downcast

Las dos primeras líneas ya las conocemos del proceso de upcasting. Lo que nos interesa es la tercerca, en ésta ocurre la conversión desde la referencia del supertipo Figura a la referencia deñ subtipo Circulo: downcasting.

En línea con lo anterior, hay que notar que debemos usar de forma explícita al tipo de dato que vamos a convertir. En este caso: rodeando la operación del lado derecho con (Circulo). Esta es la principal diferencia sintáctica en código de C# entre upcasting y downcasting.

Si además,

Console.WriteLine (objCirculo2.CalcularCircunferencia());// No generá error
Console.WriteLine (objCirculo2 == objFigura); // True
Console.WriteLine (objCirculo2 == objCirculo); // True

La invocación a CalcularCircunferencia no causa ningún error debido a la conversión realizada en las línea de ejemplo al principio de esta subsección. Las siguientes dos líneas de código equiparán las referencias de los identificadores objCirculo2objCirculo, y objFigura.

Por otro lado, en un caso hipotético, tenemos las siguientes líneas de código:

Esfera objEsfera = new Esfera();
Figura3D objFigura3d = objEsfera;
Circulo objCirculo = (Circulo) objFigura3d; // Error de downcasting: objFigura3d no es un Circulo

Aquí tenemos un ejemplo explícito en una situación donde el downcasting falla: debido a que estamos tratando de convertir (o promocionando) un subtipo (Figura3D) a uno incompatible. Cuando sucede este fenómeno, en tiempo de ejecución se lanzará la excepción InvalidCastException [4].

2. Operadores as e is

Ahora pasemos a hablar de un par de operadores (que podrían llamarse auxiliares) para la comprobación de conversiones: as e is.

2.1 El operador as

El operador as [5] realiza la misma operación de downcast, sin embargo omite la generación de una excepción y retorna null. Véamoslo con un ejemplo:

Figura objFigura = new Figura();
Circulo objCirculo = objFigura as Circulo; // el valor de objCirculo es null

En la segunda línea podemos detallar que estamos tratando de realizar una promoción de tipo no compatible, dado que "una figura no es un círculo". Como estamos haciendo uso del operador as no se generará una excepción; en su lugar se asignará null a la referencia objCirculo.

En la Figura 3 [1] nos aclaran la ventaja de hacer promoción de tipos en lugar de utilizar el operador as.
Ventaja de usar conversión en lugar del operador as.
Figura 3. Conversión o promoción vs uso de operador as [1].
Vale agregar que el operador as no está permitido en conversiones entre tipos primitivos numéricos no está permitada:

long numeroLargo = 13 as long; // Error en tiempo de compilación

2.2 El operador is

Con el operador is podemos comprobar o validar que una conversión ha resultado satisfactoria. Por ejemplo:

Figura[] figuras = new Figura[3];
figuras[0] = new Circulo();
figuras[1] = new Triangulo();
figuras[2] = new Rectangulo();

for (int i = 0 ; i < figuras.Length; ++i)
{
if (figuras[i] is Circulo)
{
Console.WriteLine ("La circunferencia es: {0}.", figuras[i].CalcularCircunferencia());
}
}

En if (figuras[i] is Circulo) se prueba que el objeto en el índice es del tipo de dato Circulo.

Conclusiones

La conversión (o promoción) de tipos es un proceso cotidiano en programación, por eso resultó importante introducir este tema en este artículo. Además, porque en la vida del programador el enfoque orientado a objetos exigue comprender, y sobretodo, usar estos conceptos en los desarrollos para poder usar mecanismos inherentes a este enfoque: herencia y polimorfismo de manera eficiente y explotar los beneficios del lenguaje en cuestión. Finalmente, vimos otras dos construcciones del lenguaje C# para hacer el proceso de downcasting (a través del operador as), y preguntar en código si una determinada referencia corresponde con determinado tipo (a través del operador is).

Glosario

- Conversión
- Downcasting
- Promoción
- Upcasting

Enlaces & Literatura

[1]: C# 5.0 in a Nutshell by Joseph Albahari and Ben Albahari. Copyright 2012 Joseph Albahari and Ben Albahari, 978-1-449-32010-2.
[6]: is (C# Reference) - http://msdn.microsoft.com/en-us/library/scekt9xw.aspx
[7]: 6.1.4 Implicit reference conversions (C#) - http://msdn.microsoft.com/en-us/library/aa691284(v=vs.71).aspx


H

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.