2009-02-06 31 views
6

Aquí es una versión simplificada de mi clase:¿Puede una clase base determinar si una clase derivada ha reemplazado a un miembro virtual?

public abstract class Task 
{ 
    private static object LockObject = new object(); 

    protected virtual void UpdateSharedData() { } 
    protected virtual void UpdateNonSharedData() { } 

    public void Method() 
    { 
     lock(LockObject) 
     { 
      UpdateSharedData(); 
     } 
     UpdateNonSharedData(); 
    } 
} 

que estoy tratando de ocultar el código de bloqueo de las clases derivadas. Pero solo quiero obtener el bloqueo si la clase derivada anula UpdateSharedData; si no lo hace, no quiero que el método bloquee y espere en todas las demás instancias en ejecución que actualicen los datos compartidos antes de que actualice los datos no compartidos.

Por lo tanto, lo (aparentemente) obvio para Method es comprobar y ver si la implementación de la instancia actual de UpdateSharedData ha anulado la implementación de la clase base. Estoy bastante seguro de que esto no es posible sin usar el reflejo, y probablemente no sea deseable hacerlo.

he pensado en algunas soluciones a este, pero todos son bastante incómoda:

  • añadir un alojamiento protegido que bool de la clase derivada conjuntos constructor, y comprobar que la propiedad para ver si es una cerradura necesario. Eso está haciendo un trabajo bastante terrible al ocultar el código de bloqueo de las clases derivadas.
  • Haga que el método UpdateSharedData sea una propiedad delegada, tenga cualquier clase derivada establezca la propiedad de un método privado en su constructor, y obtenga el bloqueo únicamente si el delegado no es nulo. Eso es mejor, pero todavía es una mierda.

Respuesta

4

Lo que si ha definido una tarea abstracta y una interfaz IHasSharedData, a continuación, en el método de comprobar si la tarea derivada implementa IHasSharedData antes de hacer el bloquear. Solo las clases que implementan la interfaz deben esperar. Me doy cuenta de que esto evita responder a la pregunta real, pero creo que sería una solución más limpia que el uso de la reflexión. Es de esperar que encuentres un nombre mejor para la interfaz que se asemeje más a lo que realmente hacen las clases.

public interface IHasSharedData 
{ 
    void UpdateSharedData(); 
} 

public abstract class Task 
{ 
    private static object LockObject = new object(); 

    protected virtual void UpdateNonSharedData() { } 

    public void Method() 
    { 
     if (this is IHasSharedData) 
     { 
      lock(LockObject) 
      { 
       UpdateSharedData(); 
      } 
     } 
     UpdateNonSharedData(); 
    } 
} 

public class SharedDataTask : Task, IHasSharedData 
{ 
    public void UpdateSharedData() 
    { 
     ... 
    } 
} 
+0

Me acabo de dar cuenta de que IHasSharedData se parece mucho al código de LOLCats. :-) ICanHazSharedData? – tvanfosson

+0

¡Eso fue lo primero que noté también! Va a ser muy difícil para mí resistirme a darle un nombre así. Esta es exactamente la respuesta que estaba buscando, sin embargo. –

4

Usted puede hacer esta comprobación con una pizca de reflexión:

bool IsUpdateSharedDataOverridden() 
{ 
    Type t = this.GetType(); 
    MethodInfo m = subType.GetMethod("UpdateSharedData"); 

    return m.DeclaringType == t && m.GetBaseDefinition().DeclaringType == typeof(Task); 
} 
+0

Sería más correcto comparar m.DeclaringType directamente a t. Es muy posible que dos tipos diferentes tengan el mismo nombre. – JaredPar

+0

Eso también reportaría verdadero si usó "nuevo" para volver a declarar (no anular) el método. –

+0

Buenos puntos. Corregido en consecuencia. –

0

En realidad, se trata de dos objetos diferentes:

public abstract class Task {  
    protected virtual void UpdateNonSharedData() { } 

    public virtual void Method()  
    { 
     UpdateNonSharedData();  
    } 
} 

public abstract class TaskWithSharedData : Task {  
    private static object LockObject = new object();  

    protected virtual void UpdateSharedData() { } 

    public overrides void Method()  
    {  
     lock(LockObject) 
     {   
      UpdateSharedData();  
     } 
     base.Method(); 
    } 
} 

embargo, la solución más ideal será el patrón de estrategia.

Cuestiones relacionadas