Я могу использовать шаблон "декоратор" для обертывания тела метода?

У меня есть набор методов с переменными подписями. Эти методы взаимодействуют с хрупкой передачей данных, таким образом, мы часто используем класс помощника для выполнения, повторяет/повторно подключает, и т.д. Как так:

MyHelper.PerformCall( () => { doStuffWithData(parameters...) });

И это хорошо работает, но это может сделать код небольшим cluttery. То, что я предпочел бы делать, украшают методы, которые взаимодействуют с передачей данных как так:

[InteractsWithData]
protected string doStuffWithData(parameters...)
{
     // do stuff...
}

И затем по существу, каждый раз, когда doStuffWithData назван, тело того метода было бы передано в как Action кому: MyHelper.PerformCall(). Как я делаю это?

22
задан Matthew Groves 3 June 2010 в 14:10
поделиться

7 ответов

Атрибуты .NET - это метаданные, а не декораторы / активные компоненты, которые вызываются автоматически. Невозможно добиться такого поведения.

Вы можете использовать атрибуты для реализации декораторов, поместив код декоратора в класс Attribute и вызвав метод с помощью вспомогательного метода, который вызывает метод в классе Attribute с помощью Reflection. Но я не уверен, что это было бы большим улучшением по сравнению с прямым вызовом метода-декоратора.

«Атрибут декоратора»:

[AttributeUsage(AttributeTargets.Method)]
public class MyDecorator : Attribute
{
    public void PerformCall(Action action)
    {
       // invoke action (or not)
    }
}

Метод:

[MyDecorator]
void MyMethod()
{
}

Использование:

InvokeWithDecorator(() => MyMethod());

Вспомогательный метод:

void InvokeWithDecorator(Expression<Func<?>> expression)
{
    // complicated stuff to look up attribute using reflection
}

Взгляните на фреймворки для аспектно-ориентированного программирования на C #. Они могут предложить то, что вы хотите.

16
ответ дан 29 November 2019 в 04:40
поделиться

Видя, что вы готовы добавить строку кода к каждому методу, который в этом нуждается, почему бы просто не вызвать MyHelper из самого метода, как здесь?

protected string doStuffWithData(parameters...)
{
     MyHelper.PerformCall( () => { doStuffWithDataCore(parameters...) });
}

private string doStuffWithDataCore(parameters...) {
    //Do stuff here
}
0
ответ дан 29 November 2019 в 04:40
поделиться

Ознакомьтесь с аспектно-ориентированными фреймворками . Но имейте в виду, что, хотя они скрывают сложность каждого метода, наличие функций AoP может усложнить поддержку вашей программы. Это компромисс.

1
ответ дан 29 November 2019 в 04:40
поделиться

Этот тип проблем в значительной степени является тем, что AOP (аспектно-ориентированное программирование) стремится решить. Такие инструменты, как PostSharp, могут решить общие проблемы, переписав скомпилированный код. Подкаст Скотта Хансельмана недавно обсуждал АОП, так что, возможно, стоит послушать.

3
ответ дан 29 November 2019 в 04:40
поделиться

Итак, я только что посетил сеанс АОП в эти выходные, и вот способ сделать это с помощью PostSharp:

[Serializable]
public class MyAOPThing : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        Console.WriteLine("OnInvoke! before");
        args.Proceed();
        Console.WriteLine("OnInvoke! after");
    }
}

А затем украсить методы с помощью [MyAOPThing] . Легкий!

17
ответ дан 29 November 2019 в 04:40
поделиться

Похоже, то, что вы хотите, похоже на поведение IoC-контейнера или фреймворка для запуска тестов, где он на самом деле не выполняется из вашей сборки, а запускает динамически эмитированную сборку, построенную вокруг вашего кода. (Более умные люди, чем я, называли это AOP в других ответах)

Так что, возможно, в заглушке для вашего приложения вы могли бы просканировать другие сборки, собрать эти эмитированные сборки (которые вызывают MyHelper.PerformCall с телом декорированных методов), а затем ваша программа запускается с эмитированным кодом.

Ни в коем случае я не стал бы начинать путь попытки написать это, не оценив, может ли какой-нибудь существующий фреймворк AOP выполнить то, что вам нужно. HTH>

1
ответ дан 29 November 2019 в 04:40
поделиться

Без генерации годе вы ничего не сможете с этим поделать. Возможно, вы могли бы улучшить синтаксис.

А как насчет использования метода расширения?

class static MyHelper
{
  Wrap<T>(this object service, Action<T> action)
  {
    // check attribute and wrap call
  }

}

Использование:

RawFoo foo = ...
foo.Wrap(x => x.doStuffWithData(parameters...));

Это тривиально, но вы не можете убедиться, что использовался Wrap.

Вы можете реализовать общий декоратор. Этот декоратор будет использоваться один раз для упаковки службы, а затем вы не сможете вызвать ее без упаковки.

class Decorator<T>
{
    private T implementor;

    Decorator(T implementor)
    {
      this.implementor = implementor;
    }

    void Perform<T>(Action<T> action)
    {
      // check attribute here to know if wrapping is needed
      if (interactsWithData)
      {
        MyHelper.PerformCall( () => { action(implementor) });
      }
      else
      {
        action(implementor);
      }
    }
}

static class DecoratorExtensions
{
    public static Decorator<T> CreateDecorator<T>(T service)
    {
      return new Decorator<T>(service);
    }
}

Использование:

// after wrapping, it can't be used the wrong way anymore.
ExtendedFoo foo = rawFoo.CreateDecorator();
foo.Perform(x => x.doStuffWithData(parameters...));
4
ответ дан 29 November 2019 в 04:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: