在C#中实现方法装饰器
在 python
中,可以使用 函数装饰器
来扩展函数和方法的功能。
具体来说,我正在把一个设备库从 python
迁移到 C#
。与设备的通信可能会产生错误,这些错误需要用自定义的异常重新抛出。
在 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
),请告诉我。
4 个回答
8
正如其他人提到的,像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容器,这就不是问题了)。
5
你可以通过面向切面编程来实现类似的功能。我以前只用过PostSharp,不过它在商业使用上是收费的。
还有其他的AOP解决方案,你也可以使用Mono.Cecil来实现类似的效果,但这样会需要更多的工作。
Reza Ahmadi写了一篇很不错的入门文章,叫做使用C#和PostSharp的面向切面编程。这篇文章能让你对这个概念有一个比较清晰的了解,以及它是如何工作的。