在C#中实现方法装饰器

Raz*_*zer 9 c# python design-patterns decorator

python可以实现function decorators扩展功能和方法的行为.

特别是我正在将设备lib迁移pythonC#.与设备的通信可能会产生错误,应该使用自定义异常重新启动.

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))
Run Code Online (Sandbox Code Playgroud)

此方法调用将扩展为

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

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

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

如果需要实现decorator(device_error_wrapper),请告诉我们.

Zde*_*vic 7

正如其他人所指出的那样,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;
    }
} 
Run Code Online (Sandbox Code Playgroud)

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;
    }
} 
Run Code Online (Sandbox Code Playgroud)

但是有一些缺点:

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


Jen*_*sen 5

您可以使用Aspect Oriented Programming实现类似的目标。我过去只使用过PostSharp,但它不是免费用于商业用途的。

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

Reza Ahmadi 写了一篇不错的介绍文章,称为使用 C# 和 PostSharp 的面向方面编程。它可以让您对预期的内容及其工作方式有足够的了解。