Могу ли я использовать шаблон декоратора для обертывания тела метода?
У меня есть множество методов с различными сигнатурами. Эти методы взаимодействуют с хрупким соединением данных, поэтому мы часто используем вспомогательный класс для выполнения повторных попыток/повторных подключений и т.д. Так же:
MyHelper.PerformCall( () => { doStuffWithData(parameters...) });
И это отлично работает, но это может сделать код немного загроможденным. То, что я хотел бы сделать, - это украсить методы, которые взаимодействуют с подключением данных следующим образом:
[InteractsWithData]
protected string doStuffWithData(parameters...)
{
// do stuff...
}
И затем, по существу, всякий раз, когда вызывается doStuffWithData
, тело этого метода будет передано как Action
to MyHelper.PerformCall()
. Как это сделать?
Ответы
Ответ 1
. Атрибуты .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
}
Взгляните на рамки для аспектно-ориентированного программирования на С#. Они могут предложить то, что вы хотите.
Ответ 2
Итак, я просто пошел на сессию АОП в эти выходные, и вот способ сделать это с PostSharp:
[Serializable]
public class MyAOPThing : MethodInterceptionAspect
{
public override void OnInvoke(MethodInterceptionArgs args)
{
Console.WriteLine("OnInvoke! before");
args.Proceed();
Console.WriteLine("OnInvoke! after");
}
}
И затем украсьте методы с помощью [MyAOPThing]
. Легко!
Ответ 3
Этот тип проблемы в значительной степени предназначен для решения AOP (аспектно-ориентированного программирования).
Такие инструменты, как PostSharp, могут обеспечить сквозные проблемы путем перезаписи скомпилированного кода.
Подкаст Scott Hanselman недавно обсуждал AOP, поэтому, возможно, стоит послушать.
Ответ 4
Без использования генерации gode вы не можете много сделать против этого. Возможно, вы могли бы лучше использовать синтаксис.
Но как насчет использования метода расширения?
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...));
Ответ 5
Отметьте аспектно-ориентированные рамки. Но имейте в виду, что, хотя они скрывают сложность в каждом методе, наличие функций AoP может затруднить работу вашей программы. Это компромисс.
Ответ 6
Похоже, что вы хотите, так же, как и поведение контейнера контейнера IoC или платформы тестового бегуна, где он фактически не выполняется из вашей сборки, но запускает динамически выпущенную сборку, построенную на вашем коде. (Более умные люди, чем я назвал этот АОП в других ответах)
Поэтому, возможно, в заглушке для вашего приложения вы можете сканировать другие сборки, создавать эти выпущенные сборки (которые вызывают MyHelper.PerformCall с телом декорированных методов), тогда ваша программа работает против испускаемого кода.
Ни в коем случае я не стану начинать с того, что пытаюсь написать это, не оценивая, может ли какая-то существующая структура AOP выполнить то, что вам нужно. НТН >
Ответ 7
Увидев, что вы хотите добавить строку кода ко всем методам, которым это необходимо, почему бы просто не сделать вызов MyHelper из самого метода, например?
protected string doStuffWithData(parameters...)
{
MyHelper.PerformCall( () => { doStuffWithDataCore(parameters...) });
}
private string doStuffWithDataCore(parameters...) {
//Do stuff here
}