2013-03-10 10 views
5

In python ist möglich, function decorators zu implementieren, um das Verhalten von Funktionen und Methoden zu erweitern.Implementieren Methode Dekoratoren in C#

Insbesondere migrieren ich eine Gerätelib von python zu C#. Die Kommunikation mit dem Gerät kann Fehler erzeugen, die mit einer benutzerdefinierten Ausnahme erneut ausgelöst werden sollten.

In python würde ich so schreibe:

@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)) 

Diese Methode Aufruf

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

Mit diesem Muster erweitern würde können Sie leicht wickeln und Methoden erweitern, ohne jede try-except Klausel schreiben in jede Methode.

Ist es möglich, ein ähnliches Muster mit C# zu implementieren?

Wenn die Implementierung des Dekorators() benötigt wird, bitte sagen.

+0

eine Alternative, die ich kenne: // stackoverflow.com/questions/7384228/how-can-i-avoid-duplicated-try-catch-blocks) oder Sie könnten versuchen, Postsharp (auch in einer der Antworten erwähnt). Wenn es wirklich möglich ist, Attribute als Methodendekoratoren für diesen Zweck zu verwenden, weiß ich es nicht (ich bezweifle es, aber ich wette, dass jemand das für dich beantworten wird). – bas

Antwort

4

Sie können etwas ähnliches unter Verwendung Aspect Oriented Programming erreichen. Ich habe in der Vergangenheit nur PostSharp verwendet, aber es ist nicht frei für kommerzielle Nutzung.

Es gibt andere AOP-Lösungen da draußen und Sie können sicherlich etwas ähnliches mit Mono.Cecil erreichen, aber es würde mehr Arbeit erfordern.

Reza Ahmadi hat einen netten kleinen Artikel mit dem Titel Aspect Oriented Programming Using C# and PostSharp geschrieben. Es kann Ihnen eine klare Vorstellung davon geben, was Sie erwarten und wie es funktioniert.

+0

verwandt mit Mono.Cecil. Sie können einen Blick auf Simon Cropps Fody werfen. Es ist ein nettes kleines Plug-in, das Ihnen helfen kann, vorgefertigte Aspekte in Ihren Code zu weben. Es enthält auch eine Vorlage, mit der Sie Ihre eigenen Aspekte erstellen können. – Aron

1

Es gibt keine leicht Weise solche Dekorateure in C# zu implementieren - custom Attributes standardmäßig sind nur beschreibend. Es gibt jedoch Projekte, die den C# -Compiler oder die Laufzeit erweitern, so dass Sie diesen tatsächlich verwenden können. Ich denke, das beste ist PostSharp. Damit können Sie einen solchen Methoden-Decorator ("Aspekt" im Allgemeinen) definieren und die Methode wird während der Kompilierung wie gewünscht umgebrochen.

Ich habe auch gesehen, dass dies implementiert, indem Sie Ihre Klassen von Dekoratoren Klassen tatsächlich wickeln, aber das ist viel der Arbeit und ich glaube nicht, dass es in einer wirklich allgemeinen Weise getan werden kann. Wikipedia zeigt dies in Decorator Pattern article

1

Wie andere schon erwähnt haben, suchen Sie AOP. PostSharp ist eine gute Post-Compile-Lösung, aber Castle DynamicProxy ist eine Laufzeit-AOP-Lösung.

3

Wie bereits erwähnt, können Sie mit Tools wie PostSharp während der Kompilierung (nach der Kompilierung) die Cross Cutting-Logik einbinden.

Die Alternative ist es in der Laufzeit zu tun. Mit einigen IoC-Tools können Sie die Interceptors definieren, die dann zu Proxy-Klassen für Ihre Implementierung hinzugefügt werden. Das hört sich viel komplexer an, als es wirklich ist, deshalb werde ich ein Beispiel zeigen, das auf Castle DynamicProxy basiert.

Zuerst definieren Sie Ihre Klasse, die umgebrochen werden soll.

[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 dient hier als Dekorateur. Die Klasse selbst ist mit einem Interceptor Attribut versehen, das den Handler für die Aufrufe der Schnittstellenmethode angibt. Dies kann auch in die Konfiguration eingefügt werden, sodass es der Klasse verborgen bleibt.

Die Interzeptorimplementierung enthält die Dekorateur Logik

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; 
    } 
} 

Es gibt einige Nachteile, aber:

  • mit Dynamic können Sie nur Schnittstellen und virtuelle Methoden wickeln. von unter Verwendung von Delegierten (nicht ein Problem, das, wenn Sie bereits verwenden IoC-Container) (Beispiel in dieser Antwort http
  • Sie müssen das Objekt über IoC-Container und nicht direkt instanziiert