我有一堆不同签名的方法。这些方法与脆弱的数据连接交互,因此我们经常使用辅助类来执行重试/重新连接等。如下所示:
MyHelper.PerformCall( () => { doStuffWithData(parameters...) });
这样可以正常工作,但它可以使代码变得有点混乱。我更喜欢做的是装饰与数据连接交互的方法,如下所示:
[InteractsWithData]
protected string doStuffWithData(parameters...)
{
// do stuff...
}
然后基本上,每当调用doStuffWithData
时,该方法的主体将作为Action
传递给MyHelper.PerformCall()
。我该怎么做?
答案 0 :(得分:16)
.NET属性是元数据,而不是自动调用的装饰器/活动组件。没有办法实现这种行为。
您可以使用属性来实现装饰器,方法是将装饰器代码放在Attribute类中,并使用辅助方法调用该方法,该方法使用Reflection调用Attribute类中的方法。但是我不确定这会比直接调用“装饰器方法”有很大改进。
“装饰-属性”:的
[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#中面向方面编程的框架。这些可能会提供你想要的东西。
答案 1 :(得分:16)
所以,我本周末刚刚参加了AOP会议,这是使用PostSharp进行的一种方式:
[Serializable]
public class MyAOPThing : MethodInterceptionAspect
{
public override void OnInvoke(MethodInterceptionArgs args)
{
Console.WriteLine("OnInvoke! before");
args.Proceed();
Console.WriteLine("OnInvoke! after");
}
}
然后用[MyAOPThing]
装饰方法。简单!
答案 2 :(得分: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...));
答案 3 :(得分:3)
这类问题几乎就是AOP(面向方面编程)旨在解决的问题。 PostSharp等工具可以通过重写编译代码来提供跨领域的关注。 Scott Hanselman的播客最近讨论了AOP,所以值得倾听。
答案 4 :(得分:1)
结帐aspect oriented frameworks。但请注意,虽然它们隐藏了每种方法的复杂性,但AoP功能的存在可能会使您的程序难以维护。这是一个权衡。
答案 5 :(得分:1)
看起来你想要的东西类似于IoC容器或测试运行器框架的行为,它不是从程序集中实际执行,而是运行围绕代码构建的动态发出的程序集。 (比我在其他答案中称之为AOP更聪明的人)
因此,也许在您的应用程序的存根中,您可以扫描其他程序集,构建那些发出的程序集(使用装饰方法的主体调用MyHelper.PerformCall),然后您的程序将针对发出的代码运行。
在没有评估一些现有的AOP框架是否可以实现您的需求的情况下,我决不会开始尝试编写本文。 HTH&GT;
答案 6 :(得分:0)
看到你愿意为每个需要它的方法添加一行代码,为什么不从方法本身调用MyHelper,就像这样?
protected string doStuffWithData(parameters...)
{
MyHelper.PerformCall( () => { doStuffWithDataCore(parameters...) });
}
private string doStuffWithDataCore(parameters...) {
//Do stuff here
}