lunes, 30 de diciembre de 2013

Clases y Miembros Abstractos en C#

Tabla de Contenido

0. Introducción
1. Clases y Miembros Abstractos en C#
1.1 Clases Abstractas
1.1.1 Definición
1.1.2 Restricciones
1.2 Miembros Abstractos
1.2.1 Definición
1.2.1.1 Métodos
1.2.1.2 Campos y Propiedades
1.2.2 Restricciones
2. Modificador abstract
3. Diferencias y Analogías entre Clases Abstractas e Interfaces
4. Rendimiento
5. Ejemplos
5.1 Simple
5.2 Calculadora
6. Conclusiones
7. Glosario
8. Enlaces & Literatura

0. Introducción

Paso en esta ocasión a escribir acerca de clases y miembros abstractos. Estos corresponden con representaciones abstractas (nivel de detalle generalizado) para miembros función y clases. Es decir, provee el mecanismo por el cual se puede definir clases, métodos, propiedades, indexadores, eventos con una implementación vacía dando cabida para que subtipos de la jerarquía, clases en particular, creen una definición a partir de un contrato abstracto y aprovechando la herencia, también, de miembros concretos. Ahora entramos en más detalles respecto a este concepto. Bienvenidos.

1. Clases y Miembros Abstractos en C#

Añadiendo a lo que mencioné al empezar con la introducción, en nuestro modelo del mundo del problema (o dominio del problema) podemos incluir conceptos de alto nivel conceptual y dejarlos preparados para definiciones específicas (i.e.: clases concretas, miembros función (i.e.: métodos, propiedades, indexadores, eventos). Además, nos permite aplicar conceptos de programación orientada a objetos, en especial polimorfismo [Polimorfismo en C#].

1.1 Clases Abstractas

Una clase abstracta se puede concebir como un elemento de dato que posee dos tipos de miembros:
  • Abstractos
  • Concretos
Con miembros abstractos me refiero a métodos, propiedades, &c., con ausencia de implementación. Y los miembros concretos son aquellos que proveen una implementación heredable por los tipos derivados. Además, pueden ser o no virtuales [Miembros Función Virtuales en C#].

1.1.1 Definición de una clase abstracta

Para definir una clase abstracta basta con incluir el modificador abstract (en la sección 2 hablaré con más detalle de este modificador) previo a la palabra clave class. Es como sigue:

abstract class MiClaseAbstracta
{
// miembros abstractos y concretos
}

1.1.2 Restricciones

A continuación una lista de las restricciones sobre clases abstractas más comunes:
  • Una clase abstracta no puede ser instanciada.
  • No es posible combinar la definición de una clase abstracta con el modificador sealed [Clases Estáticas en C#].
  • Una clase concreta que herede de una clase abstracta, deberá implementar cada uno de los métodos de esta última.

1.2 Miembros Abstractos

Una clase abstracta puede contener miembros abstractos (estamos hablando de indexadores, propiedades, métodos, y eventos).

1.2.1 Definición

Un miembro abstracto se define como una función capaz de poseer una la firma o contrato que ha de ser especializada por un subtipo de la jerarquía de herencia a partir de la clase abstracta (como superclase).

1.2.1.1 Métodos Abstractos

Un método abstracto consiste en la especificación de la firma en la declaración del método en cuestión. Esté deberá ser implementado por los tipos de datos heredados. Implícitamente un método abstracto es virtual (virtual) y deberá ser sobrescrito (override).

Ejemplo de un método abstracto:

public abstract double CalcularArea ();

Tenga en cuenta que el cuerpo del método ha sido reemplazada por un punto y coma (;). El compilador generará el siguiente error si especificamos (inclusive vacío) un cuerpo de implementación para el método abstracto:

error CS0500: `Tipo.CalcularArea()' cannot declare a body because it is marked abstract

La clase que herede de la clase abstracta que contiene el método anterior, debe seguir la siguiente regla de implementación del contrato:

public override double CalcularArea () 
// implementación del método abstracto
}

Se debe usar el modificador override para aceptar el contrato de la clase abstracta.

1.2.1.2 Campos y propiedades abstractas

Una propiedad abstracta que sea parte de un campo de clase puede declararse de la siguiente manera:

protected double numero;

public abstract double Numero
{
get;
set;
}

Una vez esta propiedad sea implementada por un tipo de dato heredado, quedará definida de la siguiente manera:

public override double Numero
{
get
{
return numero;
}
set
{
numero = value;
}
}

1.2.2 Restricciones

Restricciones más comunes de métodos y miembros abstractos:
  • Un método abstracto no puede ser de tipo private.
  • Un método abstracto no puede contener el modificador virtual.
  • Un miembro abstracto no puede ser marcado como static.

2. Modificador abstract

En la sección completa 1 se pudo apreciar el uso de la construcción léxica abstract. Es evidente que es el elemento léxico requerido para marcar una clase o un miembro como abstracto y que da la noción de carencia de implementación tanto en una clase o en un miembro.

3. Diferencias y Analogías entre Clases Abstractas e Interfaces

Esta es el conjunto de diferencias y analogías más comunes entre estos tipos de construcciones orientadas a objetos:
  • Una clase abstract puede contener tanto miembros concretos y abstractos. La interfaz únicamente abstractos (virtuales).
  • Una clase puede implementar una o más interfaces, mientras que sólo puede heredar de una única clase abstracta.
  • Tanto de una clase como una interfaz no se pueden crear instancias.
  • Los miembros abstractos, tanto de una intefaz como de una clase abstracta, deben ser implementados en el tipo heredado.
  • Una clase abstract puede intentar parecerse a una interfaz cuando todos sus miembros son de tipo virtual.

3.1 Decisión de Diseño

Vale la pena tener en cuenta la recomendación de selección hecha en [5] por Jayabu [6]:
"The selection of interface or abstract class depends on the need and design of your project. You can make an abstract class, interface or combination of both depending on your needs."

4. Rendimiento

Diríjase a [7] para ver con detalle la prueba hecha por Sam Allen [8] para diferenciar los detalles de rendimiento entre implementación (uso de interfaces) y herencia (uso de clases abstractas).

Sin embargo, quiero, hacer la prueba usando el código fuente expuesto en [7]:
Prueba de rendimiento entre interfaces y clases abstractas.
Figura 1. Prueba de rendimiento entre interfaces y clases abstractas.

Datos estadísticos generales de la prueba:



Máximo:
Interfaz: 12.26
Clase: 4.20

Mínimo
Interfaz: 4.34
Clase: 4.20

Media:
Interfaz: 7.57
Clase: 6.08

Desviación estándar:
Interfaz: 4.16
Clase: 2.76


Hardware de pruebas: CPU, Memoria.

5. Ejemplos

5.1 Simple

Archivo Figura.cs:
Archivo Circulo.cs:

5.2 Calculadora
Herencia Calculadora.
Figura 2. Herencia Calculadora.

Archivo Calculadora.cs:

Archivo CalculadoraEstandar.cs:

Archivo CalculadoraTrigonometrica.cs:

[Nota: Omito las pruebas en Ideone por resultar evidente los resultados de las operaciones. Lo que nos interesa en realidad es observar la utilidad de las clases abstractas en una jerarquía de herencia.]

Conclusiones

El trabajo en este artículo ha caído en la comprensión del concepto de abstracto en programación orientada objetos, más precisamente en su uso en el lenguaje de programación C#. Vimos que una clase abstracta es un intermedio entre una clase concreta y una interfaz, pues posee características inherentes a cada una de ellas. La prueba de rendimiento en la sección 4 nos llevo a la conclusión que el uso de clases abstractos recae en un mejor rendimiento (vale aclarar que estas pruebas se realizaron en una sola máquina). Al final escribí unos ejemplos sencillos para apreciar las ventajas de diseño de uso de clases abstractas, en particular en la jerarquía de herencia de Calculadora.

Glosario

- Abstract
- Abstracto
- Clase
- Concreto
- Miembro

Enlaces & Literatura

[2]: C# Abstract Keyword - http://www.dotnetperls.com/abstract
[3]: Understanding C# Abstract Classes - Techotopia - http://www.techotopia.com/index.php/Understanding_C_Sharp_Abstract_Classes
[5]: All about abstract classes. - CodeProject - http://www.codeproject.com/Articles/6118/All-about-abstract-classes
[6]: Jayababu - Professional Profile - CodeProject - http://www.codeproject.com/Members/Jayababu
[7]: C# Interface Versus Virtual Method Performance - http://www.dotnetperls.com/interface-virtual-performance
[8]: About: Sam Allen - http://www.dotnetperls.com/about


H

Ejercicio C# No. 2.7

Tabla de Contenido

1. Planteamiento
2. Solución
3. Discusión
4. Código Fuente C#
Conclusiones
Glosario
Literatura & Literatura

1. Planteamiento

En la Figura 1 se incluye el planteamiento original (en inglés) del Ejercicio 2.7.
Ejercicio 2.7
Figura 1. Planteamiento del Ejercicio 2.7 [1].

2. Solución

La solución a este problema consiste básicamente en la manipulación de los siguientes tipos de datos:
  • Form [2]: Clase que representa una formulario (lienzo) para la incrustación de otro tipos de controles. Aquí es donde he colocado los dos objetos requeridos a dibujar: un menú, y un área de texto.

  • MenuStrip [3]: Barra de menú que ocupa la posición superior del área (lienzo) del formulario.
  • ToolStripMenuItem [4]: Representa ítems de menú (análogos a los comandos de operaciones).
  • RichTextBox [5]: Representa un área de texto enriquecida.
En la sección 4 veremos que la implementación de la solución se realiza escribiendo todo el código manualmente, sin la ayuda del entorno de desarrollo integrado Visual Studio.

3. Discusión de la Solución

Fundamentalmente la solución consiste en un sólo archivo de código fuente C# en donde se especifica un nombre de espacio que corresponde con el nombre del capítulo del ejercio (i.e. Ch02). En este namespace se crean el tipo MyNotepad que hereda de la clase Form [2].

En el método InitializeComponent se crear los componentes necesarios para ser incluídos en el fomulario princial. Desde aquí ocurren llamadas a los métodos ayudantes (código personalizado) CreateMenuBarCreateRichTextBox. Aunque todas las operaciones de la implementación de cada método anterior se pudo haber realizado dentro del cuerpo de InitializeComponent, se optó por crear un método por cada control a agregar al formulario para evitar toneladas de código en un sólo método: va a resultar más fácil de administrar las secciones de código. En la región CUSTOM_CODE se incluye todo el código personalizado (es decir, el generado por el mismo programador).

Aclaro que InitializeComponent sólo se incluye la manipulación de las propiedades del objeto Form FrmMyNotepad. Como:
  • Imposibilidad de redimensionamiento del formulario:

    this.FormBorderStyle = FormBorderStyle.FixedDialog;
  • Desactivación del botón de minimizar:

    this.MaximizeBox = false;
  • Establecimiento de la posición inicial de la ventana del formulario:

    this.StartPosition = FormStartPosition.CenterScreen;

4. Código Fuente C#


5. Ejecución y Resultado

Para poner a prueba la implementación anterior (4), usamos los siguientes comandos desde la línea de comandos a través del compilador de C#:


Cuando hayamos ejecutado el último comando obtendremos en pantalla:
Resultado Ejercicio 2.7.
Figura 2. Resultado Ejercicio 2.7.

Conclusiones

Én la solución de este ejercicio hemos aprendido cómo crear un formulario sin el uso del IDE Visual Studio. Todo hecho a mano. Es importante conocer cómo ocurre en estas operaciones escribiendo, inspeccionando, manipulando, &c., todos los artefactos visuales que provee el modelo de programación visual del Framework .NET.

Glosario

- Formulario
- GUI

Enlaces & Literatura

[1]: Visual C# 2012 How To Programa by Paul Deitel and Harvey Deitel. Copyright 2012 Deitel & Associates, Inc., 978-0-13-337933-4.
[2]: Form Class (System.Windows.Forms) - http://msdn.microsoft.com/en-us/library/System.Windows.Forms.Form(v=vs.110).aspx
[3]: MenuStrip Class (System.Windows.Forms) - http://msdn.microsoft.com/en-us/library/vstudio/system.windows.forms.menustrip(v=vs.100).aspx
[4]: ToolStripMenuItem Class (System.Windows.Forms) - http://msdn.microsoft.com/en-us/library/system.windows.forms.toolstripmenuitem(v=vs.110).aspx
[5]: RichTextBox Class (System.Windows.Controls) - http://msdn.microsoft.com/en-us/library/system.windows.controls.richtextbox(v=vs.110).aspx
[6]: C# InitializeComponent Method - http://www.dotnetperls.com/initializecomponent


O

domingo, 29 de diciembre de 2013

Miembros Función Virtuales en C#

Tabla de Contenido

0. Introducción
1. Miembros Función Virtuales
2. Construcciones para Miembros Función Virtuales
2.1 virtual
2.2 override
2.3 Ejemplo
2.4 new
2.5 Invocación de Métodos Virtuales desde Constructores
3. Conclusiones
4. Glosario
5. Literatura & Literatura

0. Introducción

En esta oportunidad voy a centrar mi atención en un tema muy interesante a la hora de implementar el mecanismo de polimorfismo en nuestro modelo del mundo del problema. Vamos a hacer un recorrido por los elementos fundamentales de los miembros función virtuales, en especial, los métodos virtuales y su capacidad especial para facilitar el mecanismo anteriormente nombrado. Desde luego las demostraciones en código fuente C# estarán centradas en el uso de las construcciones virtual y override.

1. Miembros Función Virtuales

Cuando de miembro función virtual hablamos, nos estamos referiendo a métodos, indezadores, propiedades, eventos, que, digamos, pueden mutar (cambiar su implementación) en tipos de datos dervidados en una jerarquía de herencia. Pensemos en esto como dicen en [3;Ans/b: BFee]:
~"...con un método virtual se crea la oportunidad de crear una implementación propia sobre los tipos de datos derivados. Lo anterior resulta opcional: puede dejar la implementación por defecto de la superclase, o hacer una implementación arbitraría para cada subtipo de la jerarquía de herencia."
[Nota: con el símbolo '~´ (tilde, o aproximación en ciencias matemáticas) en el parráfo anterior estoy significando una traducción e interpretación aproximada a la del autor original].

Hasta aquí he hecho una mera introducción respecto a métodos; sin embargo, el tratamiento o concepto es análogo para los demás tipos de función (i.e.: indezadores, eventos, y propiedades).

2. Construcciones para Miembros de Función Virtuales

Llega el turno de tratar las construcciones léxicas de C# para lograr la creación y la sobreescritura de miembros de función virtuales. Son dos:
  • virtual
  • override

2.1 virtual

virtual [6] es la construcción (modificador, precisamente) del lenguaje C# para especificar que un método, propiedad, evento o indezador es virtual. Este es un ejemplo en código fuente del método virtual Area (más adelante veremos una implementación completa):

public virtual double Area ()
{
return x * y;
}

Este método podrá ser sobrescrito (overriden) en cualquier clase derivada y especificar la implementación arbitraria al tipo de dato derivado.

Pero en abstracto, ¿qué quiere decir que un miembro función sea virtual? Un método virtual, por ejemplo, será invocado dependiendo del contexto en tiempo de ejecución del tipo de dato; esto quiere decir, que cuando el programa entrá en ejecución, la CLR es capaz de determinar la versión sobrescrita del tipo de dato derivado, de lo contrario sólo ejecutará la versión del método original (perteneciente a la superclase, o clase base).

Conectado con lo anterior, estamos implícitamente hablando de polimorfismo. Véamos esta jerarquía de herencia:
Jerarquía de herencia Figura
Figura 1. Jerarquía de herencia de figura (versión simplificada).
Podemos empezar a asumir que la clase Figura posee la versión de implementación del método Area presentanda al inicio de esta sección; en cuanto a las demás clases (i.e.: Circulo, Esfera, y Cilindro) poseerán una versión sobreescrita (arbitraria) del método en cuestión para cálculos específicos al tipo de figura.

Respecto al uso de este modificador, están los siguientes puntos a tener en cuenta [5]:
  • Por convención, los miembros de clase no son virtuales. Por consiguiente, no es posible sobreescribir métodos no virtuales. El compilador de C# generá el error CS0506:

    error CS0506: `NombreTipo.NombreMetodo()': cannot override inherited member `NombreTipo.NombreMetodo()' because it is not marked virtual, abstract or override
  • El modificador virtual no puede usarse en declaraciones de miembros con otros modificadores como static, abstract u override.
  • La invocación de miembros de la superclase se puede realizar usando la construcción base. Por ejemplo:

    base.MiembroDeSuperTipo;

2.2 override

Con la construcción override [6] se modifica la implementación de un miembro (e.gr.: un método) en las clases derivadas de una jerarquía de herencia. Por otro lado, este modificador es obligatorio para implementar un método heredado del contrato de una superclase abstracta.

Si tenemos en cuenta la jerarquía de herencia de la Figura 1, las clases CirculoEsfera, y Cilindro podrían sobreescribir el método Area de la siguiente manera:

public override double Area ()
{
// implementación arbitraria a cada tipo de dato heredado
}

Con la especificación de override en el método heredado, estamos especificando la sobreescritura del método Area con la implementación subjetiva del tipo de dato en cuestión.

2.3 Ejemplo

A continuación la implementación de la jerarquía de herencia de la Figura 1 en código fuente C#:

Archivo PruebaHerenciaFigura.cs:


En las líneas 32-35, por ejemplo, se hace la implementación requerida para calcular el área de un círculo:
http://latex.codecogs.com/png.latex?\large&space;A&space;=&space;\pi&space;r^{2}

2.4 new

Vale introducir el modificador new [8] para comprender el mecanismo de ocultamiento de miembros heredados. Por ejemplo:

Archivo PruebaOcultamiento.cs:

2.5 Invocación de Métodos Virtuales desde Constructures

En [1] nos advierten acerca del riesgo de invocar métodos virtuales desde constructures:
Peligro invocación métodos virtuales desde constructores.
Figura 2. Riesgo invocación métodos virtuales desde constructores [1].

3. Conclusiones

Hemos aprendido a escribir métodos virtuales (teniendo en cuenta que la lógica abstracta de virtualidad aplica para los demás tipos de función) y sobreescribirlos para implementaciones arbitrarias sobre tipos de datos derivados. Observamos en el ejemplo (2.3) cómo alcanzar polimorfismo usando las construcciones descritas en 2.1 y 2.2. Vimos además cómo ocultar miembros de superclases a través del uso de new (como modificador).

4. Glosario

- Overriden
- Polimorfismo
- Sobreescritura
- Virtual

5. 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.
[2]: C# Virtual Method - http://www.dotnetperls.com/virtual
[3]: c# - What is the difference between an abstract function and a virtual function? - Stack Overflow - http://stackoverflow.com/questions/391483/what-is-the-difference-between-an-abstract-function-and-a-virtual-function
[4]: 10.5.3 Virtual methods (C#) - http://msdn.microsoft.com/en-us/library/aa645767(v=vs.71).aspx


H

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