在C#中实现方法装饰器

python中可以实现function decorators来扩展函数和方法的行为。

特别是我正在将设备库从python迁移到C# 。 与设备的通信可能会产生错误,应该使用自定义exception重新启动。

python我会这样写:

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

此方法调用将扩展为

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

使用此模式,您可以轻松地包装和扩展方法,而无需在每个方法中编写每个try-except子句。

是否可以使用C#实现类似的模式?

如果需要实现装饰器( device_error_wrapper ),请告诉我。

您可以使用面向方面编程实现类似的function。 我过去只使用过PostSharp ,但它不能免费用于商业用途。

还有其他AOP解决方案,您当然可以使用Mono.Cecil实现类似的function ,但这需要更多的工作。

Reza Ahmadi写了一篇很好的小介绍文章,名为Aspect Oriented Programming Using C#和PostSharp 。 它可以让您清楚地了解期望的内容和工作原理。

正如其他人所指出的那样,PostSharp等工具允许您在编译期间(实际上,之后)编织交叉逻辑。

另一种方法是在运行时执行此操作。 一些IoC工具允许您定义拦截器,然后将拦截器添加到您的实现的代理类中。 这听起来要复杂得多,所以我将展示一个基于Castle DynamicProxy的例子。

首先,您定义需要包装的类。

 [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在这里充当装饰者。 该类本身装饰有Interceptor属性,指定接口方法调用的处理程序。 这也可以放入配置中,因此它在课堂上是隐藏的。

拦截器实现包含装饰器逻辑

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

但是有一些缺点:

  • 使用DynamicProxy,您只能包装接口和虚拟方法。
  • 你需要通过IoC容器实例化对象,而不是直接实例化(如果你已经使用了IoC容器,这不是问题)

在C#中实现这样的装饰器没有简单的方法 – 自定义属性默认情况下只是描述性的。 但是有些项目可以扩展C#编译器或运行时,以便您可以实际使用它。 我认为最好的是PostSharp 。 有了它,你可以定义这样的方法装饰器(一般来说是“方面”),并且在编译期间根据需要包装方法。

我也看到过这是通过装饰器类实际包装你的类实现的,但这是很多工作,我不认为它可以以一种非常普遍的方式完成。 维基百科在Decorator Pattern文章中展示了这一点

正如其他人所说,你正在寻找AOP。 PostSharp是一个很好的后编译解决方案,但Castle DynamicProxy是一个运行时AOP解决方案。