2013-03-10 20 views
5

In python è possibile implementare function decorators per estendere il comportamento di funzioni e metodi.Decoratori del metodo di implementazione in C#

In particolare sto migrando una lib di dispositivo da python a C#. La comunicazione con il dispositivo può generare errori che dovrebbero essere controrilanciati con un'eccezione personalizzata.

In python avrei scritto così:

@device_error_wrapper("Device A", "Error while setting output voltage.") 
def set_voltage(self, voltage): 
    """ 
    Safely set the output voltage of device. 
    """ 
    self.__handle.write(":source:voltage:level {0}".format(voltage)) 

Questa chiamata di metodo si espanderebbe a

try: 
    self.__handle.write(":source:voltage:level {0}".format(voltage)) 
except Error: 
    raise DeviceError("Error while setting output voltage.", "DeviceA") 

Con questo modello si può facilmente avvolgere ed estendere i metodi senza dover scrivere ogni try-except clausola ogni metodo

E 'possibile implementare un modello simile usando C#?

Se è necessaria l'implementazione del decoratore (device_error_wrapper), informare.

+0

un'alternativa che conosco sta usando delegati (ad esempio in questa risposta http: // stackoverflow.com/questions/7384228/how-can-i-avoid-duplicated-try-catch-blocks) oppure potresti provare postsharp (citato anche in una delle risposte). Se è davvero possibile utilizzare attributi come decoratori di metodi per questo scopo, non lo so (ne dubito, ma scommetto che qualcuno risponderà per te). – bas

risposta

4

È possibile ottenere qualcosa di simile utilizzando Aspect Oriented Programming. Ho usato solo PostSharp in passato, ma non è gratuito per uso commerciale.

Ci sono altre soluzioni AOP là fuori e si può certamente ottenere qualcosa di simile usando Mono.Cecil, ma richiederebbe più lavoro.

Reza Ahmadi ha scritto un piccolo articolo introduttivo chiamato Aspect Oriented Programming Using C# and PostSharp. Può darti un'idea abbastanza chiara di cosa aspettarsi e come funziona.

+0

correlati a Mono.Cecil. Puoi dare un'occhiata a Simon Cropp's Fody. È un bel plug in che può aiutarti a tessere aspetti predefiniti nel tuo codice. Contiene anche un modello per aiutarti a costruire i tuoi aspetti personali. – Aron

1

Non c'è facile modo per attuare tali decoratori in C# - custom Attributes sono per impostazione predefinita solo descrittivo. Esistono tuttavia progetti che estendono il compilatore o il runtime C# in modo che tu possa effettivamente utilizzarlo. Penso che il migliore sia PostSharp. Con esso è possibile definire tale decoratore di metodo ("aspetto" in generale) e il metodo viene avvolto durante la compilazione come necessario.

Ho anche visto questo implementato in realtà avvolgendo le classi per classi decoratore, ma questo è un sacco di lavoro e non penso che possa essere fatto in un modo molto generale. Wikipedia lo mostra in Decorator Pattern article

1

Come altri hanno già detto, stai cercando AOP. PostSharp è una buona soluzione di post-compilazione, ma Castle DynamicProxy è una soluzione AOP di runtime.

3

Come altri hanno sottolineato, strumenti come PostSharp consentono di intrecciare la logica di taglio durante la compilazione (in realtà, dopo).

L'alternativa è farlo in runtime. Alcuni strumenti IoC consentono di definire gli intercettori che vengono poi aggiunti alle classi proxy per l'implementazione. Sembra molto più complesso di quanto sia realmente, quindi mostrerò un esempio basato su Castle DynamicProxy.

Prima si definisce la classe che deve essere spostata.

[Interceptor(typeof(SecurityInterceptor))] 
public class OrderManagementService : IOrderManagementService 
{ 
    [RequiredPermission(Permissions.CanCreateOrder)] 
    public virtual Guid CreateOrder(string orderCode) 
    { 
     Order order = new Order(orderCode); 
     order.Save(order); // ActiveRecord-like implementation 
     return order.Id; 
    } 
} 

RequiredPermission funge da decoratore qui. La classe stessa è decorata con l'attributo Interceptor che specifica il gestore per le chiamate del metodo di interfaccia. Questo può anche essere messo in configurazione, quindi è nascosto dalla classe.

L'implementazione intercettore contiene la logica decoratore

class SecurityInterceptor : IMethodInterceptor 
{ 
    public object Intercept(IMethodInvocation invocation, params object[] args) 
    { 
     MethodInfo method = invocation.Method; 
     if (method.IsDefined(typeof(RequiredPermission), true) // method has RequiredPermission attribute 
      && GetRequiredPermission(method) != Context.Caller.Permission) { 
      throw new SecurityException("No permission!"); 
     } 

     return invocation.Proceed(args); 
    } 

    private Permission GetRequiredPermission(MethodInfo method) 
    { 
     RequiredPermission attribute = (RequiredPermission)method.GetCustomAttributes(typeof(RequiredPermission), false)[0]; 
     return attribute.Permission; 
    } 
} 

Ci sono alcuni inconvenienti, però:

  • con DynamicProxy si può avvolgere solo interfacce e metodi virtuali.
  • è necessario creare un'istanza dell'oggetto via container IoC e non direttamente (che non è un problema se si utilizza già contenitore CIO)