La mejor forma de cambiar el comportamiento según el tipo

Posible duplicado:
C # – ¿Hay una mejor alternativa que esta para ‘encender el tipo’?

Considera el clásico:

class Widget { } class RedWidget : Widget { } class BlueWidget : Widget { } 

En su mayor parte, en mi interfaz de usuario, puedo tratar todos los Widget la misma manera. Sin embargo, hay pequeñas diferencias, que necesito if o switch .

Posibles enfoques:

Indicador Enum : establecido por el constructor

 enum WidgetVariety { Red, Blue } class Widget { public WidgetVariety Variety { get; protected set; } } class RedWidget : Widget { public RedWidget() { Variety = Red; } } // Likewise for BlueWidget... switch (mywidget.Variety) { case WidgetVariety.Red: // Red specific GUI stuff case WidgetVariety.Blue: // Blue specific GUI stuff } 

El uso is

 Widget w = ...; if (w is RedWidget) { (RedWidget)w ... } else if (w is BlueWidget) { (BlueWidget)w ... } 

La razón por la que he recurrido a esto es 1) La mayoría del código ya está escrito de esta manera, pero mucho más feo. 2) El 90% del código es idéntico; básicamente, una sola columna en un GridView debe manejarse de manera diferente según el tipo.

¿Cuál recomendarías? (¿O alguien tiene una mejor solución?)


Editar Sé que probablemente me recomendarán el patrón de visitante, pero eso parece complicado en el caso de diferencias menores y dispersas.

Editar 2 Así que una diferencia particular que estaba teniendo dificultades para resolver es esta columna que es diferente entre los dos tipos. En un caso, recupera un valor de bool y lo asigna a la celda de la grilla. En el otro caso, obtiene un valor de cadena.

Supongo que en este caso, debería ser obvio que podría definir:

 public object virtual GetColumn4Data(); public override GetColumn4Data() { return m_boolval; } public override GetColumn4Data() { return m_mystring; } 

Esto me pareció incorrecto inicialmente, debido al uso de un object . Sin embargo, ese es el tipo de propiedad que estoy asignando en la celda, ¡así que, por supuesto, esto tiene sentido!

Demasiado tiempo en la oficina hoy parece …

Hay otra posibilidad Use el despacho virtual:

 class Widget { public virtual void GuiStuff() { } } class RedWidget : Widget { public override void GuiStuff() { //... red-specific GUI stuff base.GuiStuff(); } } class BlueWidget : Widget { public override void GuiStuff() { //... blue-specific GUI stuff base.GuiStuff(); } } 

El polymorphism de subtipo es la mejor solución, evitar este tipo de comprobaciones es una de las razones principales por las que se creó OO.

Widget podría tener un método DoSomething() (abstracto probablemente) y luego RedWidget y BlueWidget lo anularían.

Ver también Reemplazar condicional de Martin Fowler con polymorphism :

Visto: usted tiene un condicional que elige un comportamiento diferente según el tipo de objeto.

Refactor: mueve cada pata del condicional a un método de anulación en una subclase. Haz que el método original sea abstracto.

Para su pregunta en Edición # 2, podría usar una clase genérica para hacer que el tipo varíe entre las subclases, aunque puede funcionar o no para usted dependiendo de su diseño. Probablemente conduzca a otras decisiones de diseño difíciles.

Ejemplo aproximado:

 internal abstract class BaseClass { protected object mValue; // could also be defined as a T in BaseClass public object GetColumn4Data { get { return mValue; } } } // this is a group of classes with varying type internal abstract class BaseClass : BaseClass { public T GetTypedColumn4Data { get { return (T)mValue; } set { mValue = value; } } } // these are not really necessary if you don't plan to extend them further // in that case, you would mark BaseClass sealed instead of abstract internal sealed class BoolSubClass : BaseClass { // no override necessary so far } internal sealed class StringSubClass : BaseClass { // no override necessary so far } 

Sin embargo, tenga en cuenta que realmente no puede obtener un solo tipo de referencia que tenga un tipo de devolución variable en alguna propiedad o método. Una referencia BaseClass , en el mejor de los casos, un tipo general (como object ).