viernes, 16 de mayo de 2014

Enumeraciones en C# - Parte 5

Tabla de Contenido

0. Introducción
1. Excediendo los Límites de una Enumeración
2. Argumento de Método Inválido
3. Validación para Enumeraciones con Valores Bandera
4. Conclusiones
5. Glosario
6. Literatura & Enlaces

0. Introducción

Última entrega de la serie de Enumeraciones en C#. En esta oportunidad vamos a tratar acerca de los casos de seguridad de tipos (en lo que respecta a las enumeraciones). Entenderemos acerca de las inconsistencias que se pueden generar al asignar valores por fuera del rango permitido de una enumeración. También aprenderemos cómo atacar este tipo de problemas con métodos ayudantes. ¡Empecemos!

Artículos de la serie:

Parte 1 - Introducción a la enumeraciones en C#
Parte 2 - Operaciones de las enumeraciones
Parte 3 - Valores bandera de las enumeraciones
Parte 4 - Operadores de las enumeraciones
Parte 5 - Casos de seguridad de tipos

1. Excediendo de los Límites de una Enumeración

Con exceder los límites de una excepción me refiero a las inconsistencias generadas a partir de la asignación o generación de valores para las constantes no consistentes o inválidos. (Recordemos que la conversión de un miembro de una enumeración a un tipo numérico (ie., byte, sbyte, short, ushort, int, uint, long, ulong; excepto, char), vice versa). Recomiendo la lectura de Enumeraciones en C# - Parte 2).

Pongamos por caso, la siguiente enumeración:

public enum Bordes
{
Izquierdo,
Derecho,
Superior,
Inferior
}

¿Qué ocurre si intentamos asignar un valor diferente de 0 a 3 (los valores asignados implícitamente a la enumeración)?:

Bordes borde = (Bordes) 9876; // Conversión explícita
Console.WriteLine(borde); // Salida: 9876

Ni en tiempo de compilación ni en tiempo de ejecución, se detecta esta inconsistencia de asignación de valores por fuera del rango de la enumeración.

> Prueba de ejecución.

Esto también ocurre con los operadores unarios, ++, y --. Veamos a través de código de ejemplo las inconsistencias de valor que se generan:

Bordes borde = Bordes.Izquierdo;
--borde;

Console.WriteLine("Valor de `borde`: {0}", borde);

La operación de decremento unario, --borde, no va generar ningún tipo de error. Por lo contrario, se realizará el pre-decremento sobre el valor entero adyacente a Bordes.Izquierdo (i.e., 0).

> Prueba de ejecución.

Resultado:
Valor de `borde`: -1

En las siguientes secciones se presentarán diferentes Soluciones para atacar este tipo de problemas.

2. Argumento de Método Inválido

Asumamos que tenemos un método que dibuja un elemento en un lienzo. Su lógica opera de acuerdo al borde que pasemos como argumento al parámetro del mismo. Veamos esto con código C#:

public void Dibujar(Bordes borde)
{
if (Bordes.Izquierdo == borde)
{
// Implementación
}
else if (Bordes.Derecho == borde)
{
// Implementación
}
else if (Bordes.Superior == borde)
{
// Implementación
}
else
{
// En cualquier caso: `Bordes.Inferior`
}
}

Aquí ocurren varias consistencias. Nombrándolas:

  • Pase de argumentos Bordes con valores inválidos.
  • Dibujo inconsistente: el componente de dibujo no generara los valores esperados sobre el lienzo.
¿Cómo podemos solucionar este problema?

Solución 1: Agregación de una cláusula else adicional que dispare una excepción:

public void Dibujar(Bordes borde)
{
if (Bordes.Izquierdo == borde)
{
// Implementación
}
else if (Bordes.Derecho == borde)
{
// Implementación
}
else if (Bordes.Superior == borde)
{
// Implementación
}
else if (Bordes.Inferior == borde)
{
// Implementación
}
else
{
throw new ArgumentException ("Valor para `Bordes` inválido: " + borde, "borde");
}
}

Con la generación de la excepción ArgumentException [3] contralaremos el pase de valores inválidos a nuestro método Dibujar. Esta es una forma programática elegante para defendernos de valores de argumentos no consistentes. (También lo debemos considerar para otros casos particulares en donde es potencial la generación de errores o inconsistencias a causa de uso desmedidos del rango de los argumentos).
Solucion 2: Uso del método estático IsDefined de la clase Enum.

(En la seccion 2.2 de Enumeraciones en C# - Parte 2 se profundiza más acerca del método IsDefined.)

Podemos validar el valor adyacente de una variable de un tipo de enumeración antes de pasar su valor al argumento de un método (como en el caso anterior (i.e., Solución 1)). ¿Cómo?:

Bordes borde = (Bordes) 9876;

if (Enum.IsDefined (typeof(Bordes), borde))
{
Dibujar(borde);
}

En este caso, los argumentos del metodo IsDefined no pasarán la prueba, debido a que el valor 9876 se haya por fuera del rango de valores permitidos de la enumeración Bordes (i.e., 0, 1, 2, 3).

3. Validación para Enumeraciones con Valores Bandera (Flags)

Desde [1] nos hacen entender que el método Enum.IsDefined no está definido para enumeraciones con valores bandera (flags). Sin embargo, podemos utilizar el siguiente método ayudante (helper en la lengua anglosajona):

static bool ValorBanderDefinido(Bordes borde)
{
return (Bordes.Todos & borde) != 0;
}

> Prueba de ejecución.

4. Conclusiones

Hemos explorado diferentes casos de generación de valores inconsistentes para enumeraciones. Sin embargo, también se incluyeron diferentes métodos para atacar el paso de valores no consistentes a una consistencia: disparo de excepción, uso del método estático IsDefined (clase Enum), y finalmente cómo validar si un valor bandera se haya una enumeración (sección 3).

5. Glosario

  • Argumento
  • Enum
  • Unario
  • Validación

6. Literatura & Enlaces

[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]: Enumeraciones en C# - Parte 2 | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/05/enumeraciones-en-c-parte-2.html
[3]: ArgumentException Class (System) - http://msdn.microsoft.com/en-us/library/system.argumentexception.aspx


J

No hay comentarios:

Publicar un comentario

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