2012-04-11 13 views
5

Duplicar posibles:
C# - Is there a better alternative than this to ‘switch on type’?mejor manera de cambiar el comportamiento basado en el tipo

Considérese el clásico:

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

En su mayor parte, en mi interfaz de usuario, lo puede tratar todos los Widget s lo mismo. Sin embargo, hay diferencias menores, que necesito para if o switch a través de.

enfoques posibles:

Enumeración de los indicadores establecidos por - 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 
} 

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 parte de el 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.

¿Qué recomendarías? (O alguien tiene una solución mejor?)


Editar sé que probablemente se recomendó al patrón del visitante, sino que simplemente parece complicado para dispersas, pequeñas diferencias en este caso.

Editar 2 Así que una diferencia particular, yo estaba teniendo un tiempo difícil es la clasificación de 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 cuadrícula. 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 se sentía mal a mí al principio, debido a la utilización de object. Sin embargo, ese es el tipo de propiedad que estoy asignando en la celda, entonces por supuesto ¡esto tiene sentido!

demasiado tiempo en la oficina hoy parece ...

+3

Parece que lo que dices es: dado que la mayoría del código ya está escrito mal, debes continuar la tendencia escribiendo un código más difícil de mantener. –

+0

Si sus subclases tienen un tipo que varía entre ellas, entonces tal vez la clase base debe ser genérica. Luego se convierte en 'public virtual T GetColumn4Data(); ... public override bool GetColumn4Data() {return m_boolval; } ... public override string GetColumn4Data() {return m_mystring; } ' –

+0

@Sahuagin - Guarde fragmentos de código más grandes para obtener respuestas. Los comentarios no les hacen justicia. –

Respuesta

9

Hay otra posibilidad. Utilice 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(); 
    } 
} 
+0

Por supuesto .... ver mi Edit 2 :-) –

5

Subtype polymorphism es la mejor solución, evitando este tipo de controles es una de las principales razones fue creado OO.

Widget podría tener un método DoSomething() (resumen probablemente) y luego RedWidget y BlueWidget sería anularlo.

Véase también Replace Conditional with Polymorphism de Martin Fowler:

Visto: Usted tiene un condicional que elige un comportamiento diferente en función del tipo de un objeto.

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

+2

No estoy de acuerdo con la idea de que [este tipo de] polimorfismo fue "por qué se creó OO"; es más un * efecto * de algunos "OO languages". Sin embargo, +1 para los otros bits. (Hay más tipos de polimorfismo que aburridos - ¡y peligrosos! - subtipos de envío único que se encuentran en C#/Java, por ejemplo). –

+0

@pst Veo su punto, tema interesante – jorgehmv

0

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

ejemplo áspera:

internal abstract class BaseClass 
{ 
    protected object mValue; // could also be defined as a T in BaseClass<T> 

    public object GetColumn4Data { get { return mValue; } } 
} 

// this is a group of classes with varying type 
internal abstract class BaseClass<T> : 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<T> sealed instead of abstract 
internal sealed class BoolSubClass : BaseClass<bool> 
{ 
    // no override necessary so far 
} 

internal sealed class StringSubClass : BaseClass<string> 
{ 
    // no override necessary so far 
} 

Nótese, sin embargo, de que realmente no se puede obtener un solo tipo de referencia que tiene un tipo de retorno que varía en alguna propiedad o método. Una referencia BaseClass devolverá, en el mejor de los casos, un tipo general (como object).

+0

Desafortunadamente eso no funcionará aquí. Esta diferencia de tipo particular es solo una de varias diferencias pequeñas, pero importantes. Si fuera la única diferencia, estaría de acuerdo con este enfoque. –

Cuestiones relacionadas