La solución aceptada está bien y es una solución concreta a su problema. Sin embargo, me gustaría plantear una solución alternativa más abstracta.
En mi experiencia, el uso de enumeraciones para definir el flujo de la lógica es un olor a código, ya que a menudo es un signo de diseño de clase pobre.
Me encontré con un ejemplo del mundo real de esto sucediendo en el código en el que trabajé el año pasado. El desarrollador original había creado una sola clase que importaba y exportaba lógica, y cambió entre las dos basándose en una enumeración. Ahora el código era similar y tenía algún código duplicado, pero era lo suficientemente diferente como para que hacerlo hiciera que el código fuera significativamente más difícil de leer y prácticamente imposible de probar. Terminé refactorizando eso en dos clases separadas, lo que simplificó ambos y en realidad me permitió detectar y eliminar una serie de errores no reportados.
Una vez más, debo decir que usar enumeraciones para controlar el flujo de la lógica es a menudo un problema de diseño. En el caso general, las enumeraciones deben usarse principalmente para proporcionar valores seguros para los tipos y amigables para el consumidor donde los valores posibles estén claramente definidos. Se usan mejor como propiedad (por ejemplo, como ID de columna en una tabla) que como mecanismo de control lógico.
Consideremos el problema presentado en la pregunta. Realmente no sé el contexto aquí, o lo que representa esta enumeración. ¿Es dibujar cartas? Hacer dibujos? ¿Dibujando sangre? ¿Es importante el orden? Tampoco sé cuán importante es el rendimiento. Si el rendimiento o la memoria son críticos, entonces esta solución probablemente no sea la que desea.
En cualquier caso, consideremos la enumeración:
// All possible combinations of One - Eight.
public enum ExampleEnum {
One,
Two,
TwoOne,
Three,
ThreeOne,
ThreeTwo,
ThreeOneTwo
}
Lo que tenemos aquí son varios valores de enumeración diferentes que representan diferentes conceptos de negocio.
Lo que podríamos usar en su lugar son abstracciones para simplificar las cosas.
Consideremos la siguiente interfaz:
public interface IExample
{
void Draw();
}
Entonces podemos implementar esto como una clase abstracta:
public abstract class ExampleClassBase : IExample
{
public abstract void Draw();
// other common functionality defined here
}
Podemos tener una clase concreta para representar el dibujo uno, dos y tres (que por el argumento tienen una lógica diferente). Potencialmente, podrían usar la clase base definida anteriormente, pero supongo que el concepto DrawOne es diferente del concepto representado por la enumeración:
public class DrawOne
{
public void Draw()
{
// Drawing logic here
}
}
public class DrawTwo
{
public void Draw()
{
// Drawing two logic here
}
}
public class DrawThree
{
public void Draw()
{
// Drawing three logic here
}
}
Y ahora tenemos tres clases separadas que pueden estar compuestas para proporcionar la lógica para las otras clases.
public class One : ExampleClassBase
{
private DrawOne drawOne;
public One(DrawOne drawOne)
{
this.drawOne = drawOne;
}
public void Draw()
{
this.drawOne.Draw();
}
}
public class TwoOne : ExampleClassBase
{
private DrawOne drawOne;
private DrawTwo drawTwo;
public One(DrawOne drawOne, DrawTwo drawTwo)
{
this.drawOne = drawOne;
this.drawTwo = drawTwo;
}
public void Draw()
{
this.drawOne.Draw();
this.drawTwo.Draw();
}
}
// the other six classes here
Este enfoque es mucho más detallado. Pero tiene ventajas.
Considere la siguiente clase, que contiene un error:
public class ThreeTwoOne : ExampleClassBase
{
private DrawOne drawOne;
private DrawTwo drawTwo;
private DrawThree drawThree;
public One(DrawOne drawOne, DrawTwo drawTwo, DrawThree drawThree)
{
this.drawOne = drawOne;
this.drawTwo = drawTwo;
this.drawThree = drawThree;
}
public void Draw()
{
this.drawOne.Draw();
this.drawTwo.Draw();
}
}
¿Qué tan simple es detectar la llamada drawThree.Draw () que falta? Y si el orden es importante, el orden de las llamadas de sorteo también es muy fácil de ver y seguir.
Desventajas de este enfoque:
- Cada una de las ocho opciones presentadas requiere una clase separada
- Esto usará más memoria
- Esto hará que tu código sea superficialmente más grande
- A veces, este enfoque no es posible, aunque puede haber alguna variación.
Ventajas de este enfoque:
- Cada una de estas clases es completamente comprobable; porque
- La complejidad ciclomática de los métodos de dibujo es baja (y en teoría podría burlarme de las clases DrawOne, DrawTwo o DrawThree si es necesario)
- Los métodos de dibujo son comprensibles: un desarrollador no tiene que atar su cerebro en nudos para resolver lo que hace el método
- Los errores son fáciles de detectar y difíciles de escribir.
- Las clases se componen en más clases de alto nivel, lo que significa que definir una clase ThreeThreeThree es fácil de hacer.
Considere este enfoque (o similar) cuando sienta la necesidad de tener un código de control lógico complejo escrito en las declaraciones de casos. Futuro serás feliz de haberlo hecho.
goto
cuando la estructura de alto nivel no existe en su idioma. A veces desearía que hubiera unfallthru
; palabra clave para deshacerse de ese uso particular de,goto
pero bueno.goto
en un lenguaje de alto nivel como C #, entonces probablemente haya pasado por alto muchas otras (y mejores) alternativas de diseño y / o implementación. Muy desanimado.