2010-06-03 19 views
16

Tengo un montón de métodos con distintas firmas. Estos métodos interactúan con una conexión de datos frágil, por lo que a menudo usan una clase de ayuda para realizar reintentos/reconexiones, etc., así:¿Puedo usar el patrón de decorador para envolver un cuerpo de método?

MyHelper.PerformCall(() => { doStuffWithData(parameters...) }); 

Y esto funciona bien, pero puede hacer que el código un poco cluttery. Lo que yo preferiría que hacer es decorar los métodos que interactúan con la conexión de datos de este modo:

[InteractsWithData] 
protected string doStuffWithData(parameters...) 
{ 
    // do stuff... 
} 

Y a continuación, en esencia, siempre doStuffWithData se llama, el cuerpo de ese método se pasa como un Action a MyHelper.PerformCall(). ¿Cómo hago esto?

+4

¿Pregúntale a MSFT que ingrese una función en .Net 5.0? –

Respuesta

16

.NET Los atributos son metadatos, no decoradores/componentes activos que se invocan automáticamente. No hay forma de lograr este comportamiento.

Puede usar atributos para implementar decoradores poniendo el código del decorador en la clase Attribute y llame al método con un método auxiliar que invoca el método en la clase Attribute utilizando Reflection. Pero no estoy seguro de que esto sea una gran mejora con respecto a llamar directamente al "método de decorador".

"decorador-Atributo":

[AttributeUsage(AttributeTargets.Method)] 
public class MyDecorator : Attribute 
{ 
    public void PerformCall(Action action) 
    { 
     // invoke action (or not) 
    } 
} 

Método:

[MyDecorator] 
void MyMethod() 
{ 
} 

Uso:

InvokeWithDecorator(() => MyMethod()); 

Método auxiliar:

void InvokeWithDecorator(Expression<Func<?>> expression) 
{ 
    // complicated stuff to look up attribute using reflection 
} 

Tener un vistazo a los marcos para programación orientada a aspectos en C#. Estos pueden ofrecer lo que quieres.

+0

¿Te importaría escribir una muestra de código rápido de eso? Me está costando imaginarlo. –

+2

Ejemplo agregado ... – dtb

+0

Impresionante, gracias, y creo que tienes razón, realmente no obtengo mucho beneficio de llamar al ayudante directamente. –

3

Este tipo de problema es más o menos lo que AOP (programación orientada a aspectos) pretende resolver. Herramientas como PostSharp pueden proporcionar preocupaciones transversales al volver a escribir el código compilado. El podcast de Scott Hanselman discutió recientemente el AOP, por lo que podría valer la pena escucharlo.

1

Echa un vistazo aspect oriented frameworks. Pero tenga en cuenta que, si bien ocultan la complejidad de cada método, la existencia de funciones de AoP podría dificultar el mantenimiento de su programa. Es una compensación.

1

Parece que lo que desea es similar al comportamiento de un contenedor IoC o framework de prueba de corredor, donde no se está ejecutando en realidad desde su ensamblado, pero está ejecutando un ensamblaje emitido dinámicamente construido alrededor de su código. (Gente más inteligente de lo que he llamado este AOP en otras respuestas)

Así que tal vez en un stub para su aplicación podría escanear los otros ensamblajes, construir los ensamblajes emitidos (que llaman a MyHelper.PerformCall con el cuerpo de los métodos decorados) entonces tu programa corre contra el código emitido.

De ninguna manera comenzaría por el camino de tratar de escribir esto sin evaluar si algún marco AOP existente podría lograr lo que necesita. HTH>

0

Al ver que está dispuesto a agregar una línea de código a cada método que necesita esto, ¿por qué no simplemente hacer la llamada a MyHelper desde el propio método, así?

protected string doStuffWithData(parameters...) 
{ 
    MyHelper.PerformCall(() => { doStuffWithDataCore(parameters...) }); 
} 

private string doStuffWithDataCore(parameters...) { 
    //Do stuff here 
} 
+0

Sí, eso es lo que estoy haciendo actualmente. –

+1

Entonces, ¿qué significa agregar el atributo por usted? – Sijin

+1

Creo que podría hacer que el código sea más fácil de leer y mantener. –

4

Sin el uso de la generación gode, no se puede hacer mucho en contra. Probablemente puedas mejorar la sintaxis.

Pero, ¿qué pasa con el uso de un método de extensión?

class static MyHelper 
{ 
    Wrap<T>(this object service, Action<T> action) 
    { 
    // check attribute and wrap call 
    } 

} 

uso:

RawFoo foo = ... 
foo.Wrap(x => x.doStuffWithData(parameters...)); 

Esto es trivial, pero no se puede asegurar que Wrap había sido utilizado.

Puede implementar un decorador genérico. Este decorador se usaría una vez para envolver el servicio y luego no se puede llamar sin envolver.

class Decorator<T> 
{ 
    private T implementor; 

    Decorator(T implementor) 
    { 
     this.implementor = implementor; 
    } 

    void Perform<T>(Action<T> action) 
    { 
     // check attribute here to know if wrapping is needed 
     if (interactsWithData) 
     { 
     MyHelper.PerformCall(() => { action(implementor) }); 
     } 
     else 
     { 
     action(implementor); 
     } 
    } 
} 

static class DecoratorExtensions 
{ 
    public static Decorator<T> CreateDecorator<T>(T service) 
    { 
     return new Decorator<T>(service); 
    } 
} 

Uso:

// after wrapping, it can't be used the wrong way anymore. 
ExtendedFoo foo = rawFoo.CreateDecorator(); 
foo.Perform(x => x.doStuffWithData(parameters...)); 
14

Así, acabo de ir a una sesión de AOP este fin de semana, y aquí es una manera de hacerlo con PostSharp:

[Serializable] 
public class MyAOPThing : MethodInterceptionAspect 
{ 
    public override void OnInvoke(MethodInterceptionArgs args) 
    { 
     Console.WriteLine("OnInvoke! before"); 
     args.Proceed(); 
     Console.WriteLine("OnInvoke! after"); 
    } 
} 

Y luego decorar métodos con [MyAOPThing]. ¡Fácil!

+2

Esta debería ser la mejor respuesta. Gracias por publicar. –

+0

PostSharp no es gratis –

+0

La pregunta no dice nada sobre el precio. Además, hay una licencia gratuita de Postsharp disponible: https://www.postsharp.net/download –

Cuestiones relacionadas