Реализовать способ декораторов в C#

на python можно использовать function decorators для расширения поведения функций и методов.

в частности, я переношу lib устройства из python to C#. Сообщение с прибором может произвести ошибки которые должны reraised с изготовленным на заказ исключением.

на 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 ответов


вы можете достичь чего-то подобного, используя Аспектно-Ориентированное Программирование. Я только использовал PostSharp в прошлом, но это не для коммерческого использования бесплатно.

есть и другие решения AOP, и вы, безусловно, можете достичь чего-то подобного, используя Mono.Сесил!--2-->, но это потребует больше работы.

Реза Ахмади написал небольшую вводную статью под названием аспектно-ориентированное программирование с использованием C# и PostSharp. Он может дать вам достаточно ясное представление о том, чего ожидать и как это работает.


как указывали другие, такие инструменты, как PostSharp, позволяют вам переплетать логику кросс-резки во время (на самом деле, после) компиляции.

альтернатива-сделать это в runtime. Некоторые инструменты 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. С его помощью вы можете определить такой декоратор метода ("аспект" в целом), и метод будет обернут во время компиляции, как вам нужно.

Я также видел это реализовано путем фактической упаковки ваших классов классами декоратора, но это много работы, и я не думаю, что это можно сделать действительно общим способом. Википедия показывает это в статьи шаблон "декоратор"


Как уже упоминали другие, вы ищете AOP. PostSharp-хорошее решение для компиляции сообщений, но Замок DynamicProxy является решением AOP во время выполнения.