2009-06-29 8 views
5

digamos que tengo¿Existe alguna forma de imponer un método para seguir ciertas firmas de métodos?

public delegate DataSet AutoCompleteDelegate(
     string filter, long rowOffset); 

puedo hacer que la siguiente clase para hacer cumplir ese método de firma? (Sólo un evocado idea):

public class MiddleTier 
{ 
    [Follow(AutoCompleteDelegate)] 
    public DataSet Customer_AutoComplete(string filter, long rowOffset) 
    { 
     var c = Connect(); 
     // some code here 
    } 

    [Follow(AutoCompleteDelegate)] 
    public DataSet Item_AutoComplete(string filter, long rowOffset) 
    { 
     var c = Connect(); 
     // some code here 
    } 



    // this should give compilation error, doesn't follow method signature 
    [Follow(AutoCompleteDelegate)] 
    public DataSet BranchOffice_AutoComplete(string filter, string rowOffset) 
    { 
     var c = Connect(); 
     // some code here 
    }   

} 

[EDIT]

Propósito: Yo ya puse atributos en los métodos de mi middletier. Tengo métodos como éste:

public abstract class MiddleTier : MarshalByRefObject 
{ 
    // Operation.Save is just an enum 

    [Task("Invoice", Operation.Save)] 
    public Invoice_Save(object pk, DataSet delta); 

    [Task("Receipt", Operation.Save)] 
    public Receipt_Save(object pk, DataSet delta); 


    // compiler cannot flag if someone deviates from team's standard 
    [Task("Receipt", Operation.Save)] 
    public Receipt_Save(object pk, object[] delta); 
} 

después en tiempo de ejecución, voy a iterar todos los métodos de la middletier y ponerlos a la recogida (atributos ayuda mucho aquí), luego asignarlos en las funciones de delegado de winform (facilitado por la interfaz, sistema basado en complementos) como cargado

Estoy pensando si puedo hacer que los atributos sean más autodescriptivos, por lo que el compilador puede detectar inconsistencias.

namespace Craft 
{   
    // public delegate DataSet SaveDelegate(object pk, DataSet delta); // defined in TaskAttribute 

    public abstract class MiddleTier : MarshalByRefObject 
    { 

     [Task("Invoice", SaveDelegate)]   
     public abstract Invoice_Save(object pk, DataSet delta); 

     [Task("Receipt", SaveDelegate)] 
     // it's nice if the compiler can flag an error 
     public abstract Receipt_Save(object pk, object[] delta); 
    } 
} 

Estoy pensando si poner los métodos de cada clase, sería una exageración para crear instancias de un objeto siempre Remoting. Y al ponerlos en una clase separada, podría ser más difícil facilitar la reutilización del código, digamos que Invoice_Save necesita alguna información en Receipt_Open. De hecho, incluso tengo un informe aquí (cristal), que obtuvo datos de Remoting middletier DataSet, dentro del método invocado, obtiene algo de información sobre otros métodos y se fusiona en su propio DataSet, todos están sucediendo en middletier, no en varios recorridos de ida y vuelta, todos se hacen en el lado del servidor (nivel medio)

+0

¿Qué idioma es este? – hlovdal

+0

El lenguaje es C# – Hao

+0

No puse esto como respuesta porque no lo he practicado en absoluto, pero tal vez a través de la reflexión usted podría (1) obtener todos los métodos en la clase (2) para cada método comprobar que tiene la firma y atributo dados –

Respuesta

3

Puede implementar tanto el FollowAttribute que usa en su ejemplo como write a Static Analysis (say, FxCop) rule que podría verificar si algún método etiquetado con ese atributo tiene la misma firma que el delegado mencionado. Entonces debería ser posible.

1

Ésta no es una característica del lenguaje, pero ...

Esto es algo que podría hacer la validación para: pruebas de unidad de escritura que reflejan sobre la clase y fallan si la firma no coincide con la declaración de atributo.

PostSharp también le ofrece algunas opciones interesantes para hacer esto con la compilación. No sé exactamente cómo lo usaría, pero sospecho que podría ...

11

Otras respuestas son obviamente válidas, pero nada le impedirá olvidarse de aplicar el atributo [Follow(AutoCompleteDelegate)] en su método.

creo que sería mejor hacer girar métodos en clases que implementan una interfaz:

public interface IAutoComplete 
{ 
    DataSet Complete(string filter, long rowOffset); 
} 

public class CustomerAutoComplele : IAutoComplete 
{ 
    public DataSet Complete(string filter, long rowOffset) 
    { 
     var c = Connect(); 
     // some code here 
    } 
} 

y luego usar el factory method pattern para obtener sus "completaron el auto":

public static class AutoCompleteFactory 
{ 
    public static IAutoComplete CreateFor(string purpose) 
    { 
     // build up and return an IAutoComplete implementation based on purpose. 
    } 
} 

o

public static class AutoCompleteFactory 
{ 
    public static IAutoComplete CreateFor<T>() 
    { 
     // build up and return an IAutoComplete implementation based on T which 
     // could be Customer, Item, BranchOffice class. 
    } 
} 

Una vez que tenga eso, puede echar un vistazo a inver Sion de control e inyección de dependencia para evitar la codificación difícil de la lista de implementaciones de autocompletar en su método de fábrica.

+0

Podría necesitar algunos ajustes dado que el ejemplo tiene dos métodos coincidentes ... pero en general estoy de acuerdo. Diablos, incluso los genéricos harían (IAutoComplete etc.) con una implementación de interfaz explícita. –

+3

Ugh. Pasando a los métodos de fábrica, la inversión de control y la inyección de dependencia son señales de que estás en el camino equivocado. Cada uno puede parecer lo suficientemente inocente, pero agréguelos todos juntos y obtendrá un desastre excesivamente complicado. Deberías haber dejado de "convertir los métodos en clases que implementan una interfaz". Ahora que es un plan que es fácil de entender. –

+0

Sí, estoy de acuerdo en que podría ser un poco sobredimensionado (el enfoque completo) solo para una característica de autocompletar simple, pero la belleza es que todo es incremental: realmente puede detenerse en cualquier punto y tener una solución decente. Sin embargo, podría haber sido más explícito sobre eso. –

0

Los atributos se almacenan como meta-información adicional durante la compilación - se puede consultar en tiempo de ejecución, pero durante la compilación no se tienen en cuenta

no puede limitarse un método por un atributo en él.. Puede restringir cómo se aplica el atributo (es decir, solo en métodos o si se puede aplicar más de uno).

se recomienda usar FxCop para advertir cuando los atributos no coinciden - si hay que tener cuidado a la forma en acontecimientos de la ayuda conversiones de tipo:

[Follow(AutoCompleteDelegate)] 
public DataSet Customer_AutoComplete(string filter, int rowOffset) 

sería un delegado válida.

0

Tipo de.

No se puede obtener este comportamiento en tiempo de compilación. Puede, con Atributos, meter esto en un arnés de prueba simple o forzar una falla inmediata cuando se crea una instancia de la clase que lo contiene.

examinar los dos (rápidamente hackeado) atributos:

[AttributeUsage(AttributeTargets.Class)] 
public class EnforceConforms : Attribute 
{ 
    public EnforceConforms(Type myClass) 
     : base() 
    { 
     MethodInfo[] info = myClass.GetMethods(); 

     foreach (MethodInfo method in info) 
     { 
      object[] objs = method.GetCustomAttributes(false); 

      foreach (object o in objs) 
      { 
       Attribute t = (Attribute)o; 

       if (t.GetType() != typeof(ConformsAttribute)) continue; 

       MethodInfo mustConformTo = ((ConformsAttribute)t).ConformTo; 

       ParameterInfo[] info1 = mustConformTo.GetParameters(); 
       ParameterInfo[] info2 = method.GetParameters(); 

       bool doesNotCoform = false; 

       doesNotCoform |= (mustConformTo.ReturnType != method.ReturnType); 
       doesNotCoform |= (info1.Length != info2.Length); 

       if (!doesNotCoform) 
       { 
        for (int i = 0; i < info1.Length; i++) 
        { 
         ParameterInfo p1 = info1[i]; 
         ParameterInfo p2 = info2[i]; 

         if (!p1.ParameterType.Equals(p2.ParameterType)) 
         { 
          doesNotCoform = true; 
          break; 
         } 
        } 
       } 

       if (doesNotCoform) 
       { 
        throw new Exception(myClass.Name + "." + method.Name + " does not conform to required delegate signature"); 
       } 
      } 
     } 
    } 
} 

[AttributeUsage(AttributeTargets.Method)] 
public class ConformsAttribute : Attribute 
{ 
    public MethodInfo ConformTo; 

    public ConformsAttribute(Type type) 
     : base() 
    { 
     if (type.BaseType != typeof(Delegate) && type.BaseType != typeof(System.MulticastDelegate)) throw new Exception("Can only accept delegates"); 

     ConformTo = type.GetMethod("Invoke"); 
    } 
} 

Throw EnforceConforms (typeof (myFavoriteClass)) sobre una clase, y se ajusta (typeof (myFavoriteDelegate)) sobre los métodos pertinentes y entonces (esto es la parte hacky) typeof (myFavoriteClass) .GetCustomAttributes (falso). Podrías hacerlo en un inicializador estático para fallar "realmente rápido" o hacerlo en una clase de prueba (que busca todos los métodos en el ensamblado con el atributo EnforceConforms si quieres ser elegante).

Generalmente, probablemente no deberías usar esto. Si su diseño requiere que compruebe las implementaciones de delegado correctas, debe volver a diseñarlas si es posible. Además, los bits de tiempo de no compilación lo hacen para que no se esté ahorrando mucho en el tiempo.

1

Me gustaría preguntar por qué quieres hacer esto. Si no desea que la clase se modifique a través de la herencia, puede convertirla en una clase sellada. Si le preocupa que alguien cambie la clase en el futuro, tiene 1 de 2 casos. 1) No entienden lo que están haciendo; nada puede evitar que un programador malo haga cosas malas si tienen pleno dominio para editar el texto del programa. 2) Están extendiendo la funcionalidad de la clase de una manera que actualmente no entiendes, lo que lastima la reutilización.

Cuestiones relacionadas