在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解决方案。