2010-02-09 12 views
5

Ok, estoy tratando de hacer lo siguiente:sobrecarga de métodos: embudo de llamada a la clase derivada de sobrecarga argumento

 protected bool ValidAdvert(Base item) 
     { 
      throw ThisIsAnAbstractClassException(); 
     } 

     protected bool ValidAdvert(Derived1 item) 
     { 
      return ADerived1SpecificPredicate; 
     } 

     protected bool ValidAdvert(Derived2 item) 
     { 
      return ADerived2SpecificPredicate; 
     }

Y tienen las versiones de clase derivada del método de ser llamado, cuando una clase base se pasa a el método. La clase base es abstracta, así que esto debería, en teoría, ser posible.

Antes de que alguien diga algo sobre sobrecargar el método en las clases, la lógica dentro de los métodos depende de un gran número de condiciones diferentes, ninguna de las cuales está relacionada, y ninguna relacionada directamente con la clase Base/Derivada (como el estado de inicio de sesión, etc.)

Respuesta

6

Si considera que el doble despacho es demasiado intrusivo, puede llamar al método por reflexión y resolver la sobrecarga adecuada de forma dinámica.

protected bool ValidAdvert(Base item) 
{ 
    if (item.GetType() == typeof(Base)) 
     throw new ThisIsAnAbstractClassException(); 

    Type type = typeof(CurrentClass); 

    MethodInfo method = type.GetMethod("ValidAdvert", 
             BindingFlags.Instance | BindingFlags.NonPublic, 
             null, 
             new Type[] { item.GetType() }, 
             null); 
    return (bool)method.Invoke(this, new object[] { item }); 
} 

protected bool ValidAdvert(Derived1 item) 
{ 
    return ADerived1SpecificPredicate; 
} 

protected bool ValidAdvert(Derived2 item) 
{ 
    return ADerived2SpecificPredicate; 
} 

Este patrón se llama MultipleDispatch, mientras que llamar a un método de reflexión es más lento que una llamada al método directamente (no va a ser una sobrecarga si el método se llama menos de unos pocos cientos de veces por segundo), que tiene el beneficio de no requerir modificar su jerarquía de clases como en el patrón de Despacho doble.

Esto será aún más fácil cuando C# 4.0 sale con la dinámica de palabras clave:

protected bool ValidAdvert(Base item) 
{ 
    if (item.GetType() == typeof(Base)) 
     throw new ThisIsAnAbstractClassException(); 

    dynamic dynamicThis = this; 

    return (bool)dynamicThis.ValidAdvert(item as dynamic); 
} 
+0

También es inteligente y evita meterse con las diversas clases derivadas (¡ojalá hubiera solo 2!). –

+0

notas absolutas: necesitas lanzar a bool en el retorno, y el método. Invocar toma un [] de objetos, y por lo tanto, si simplemente tratas de pasar solo 1 :) ¡Gracias! –

+0

@Ed - Creo que mantener el predicado con la clase derivada es en realidad el método preferido. El almacenamiento de la lógica derivada de clase específica en una clase base crea un acoplamiento inverso entre los dos. No veo esto como algo bueno en absoluto. La clase base no debería necesitar saber nada sobre sus clases derivadas. – tvanfosson

5

un lugar perfecto para utilizar double dispatch:

class Base 
{ 
    public abstract bool IsValid(IAdvertValidator validator); 
} 

class Derived1 : Base 
{ 
    public bool IsValid(IAdvertValidator validator) 
    { 
     return validator.ValidAdvert(this); 
    } 
} 

class Derived2 : Base 
{ 
    public bool IsValid(IAdvertValidator validator) 
    { 
     return validator.ValidAdvert(this); 
    } 
} 

interface IAdvertValidator 
{ 
    bool ValidAdvert(Derived1 d1); 
    bool ValidAdvert(Derived2 d2); 
} 
+0

inteligente, me gusta. –

+0

Un uso razonable del patrón. Una alternativa sería exponer los métodos de validación del validador y utilizarlos dentro de una implementación de IsValid, manteniendo el flujo de control de validación real en la clase. – tvanfosson

0

no estoy seguro exactamente cómo va a tener cada vez una instancia de la clase base que no es realmente una instancia de una clase derivada. Como la clase base es abstracta, no se puede crear una instancia. Supongo que coincidiría con cualquier clase derivada que no tenga una firma específica, pero eso no parece ser lo que estás buscando.

+0

Sí, siempre es una de las clases derivadas, sin embargo, también es técnicamente la clase base, lo que significa que se llama al método de clase base cuando lo hago (List ) i.Where (ValidateAdvert), por ejemplo. –

+0

Ah - Estaba leyendo tu código de muestra, no tu pregunta. En realidad, no desea que arroje una excepción, sino que debe llamar a uno de los otros métodos. En ese caso, iría con la sugerencia de @ Anton o algo similar. Tenga en cuenta que puede proporcionar el validador en un constructor para que no tenga que pasarlo como argumento. También utilicé una clase de validación estática que valida las propiedades simples y elimina la necesidad de mantener una referencia. Sin embargo, debe tener cuidado con estos, ya que pueden hacer las pruebas más difíciles si no tiene cuidado. – tvanfosson

+0

El validador es en gran medida una clase específica de página y estado, por lo que no funcionaría bien como estático, sin embargo, creo que sería bastante bueno en el método de reflexión, es una pena que no sea compatible por defecto ¡aunque! –

Cuestiones relacionadas