代码之家  ›  专栏  ›  技术社区  ›  Matthew Groves

我可以使用decorator模式包装方法体吗?

  •  19
  • Matthew Groves  · 技术社区  · 16 年前

    我有一堆不同签名的方法。这些方法与脆弱的数据连接进行交互,因此我们经常使用助手类来执行重试/重新连接等操作。例如:

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

    这样做很好,但是会让代码有点混乱。我更喜欢这样装饰与数据连接交互的方法:

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

    基本上,只要 doStuffWithData 则该方法的主体将作为 Action MyHelper.PerformCall() . 我该怎么做?

    7 回复  |  直到 16 年前
        1
  •  19
  •   Brien Foss    8 年前

    .NET属性是元数据,而不是自动调用的装饰器/活动组件。没有办法实现这种行为。

    “Decorator属性”:

    [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#中面向方面编程的框架。这些可以提供你想要的。

        2
  •  19
  •   Matthew Groves    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] . 容易的!

        3
  •  4
  •   Bouke Versteegh clauziere    5 年前

    如果不使用代码生成,您就无法对它做很多事情。你也许可以改进语法。

    但是使用扩展方法呢?

    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。

    你可以实现一个通用的装饰器。这个decorator只用于包装服务一次,没有包装就不能调用它。

    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
  •  3
  •   Dave Cluderay    16 年前

    这类问题正是AOP(面向方面编程)所要解决的。 Scott Hanselman的播客最近讨论了AOP,所以值得一听。

        5
  •  1
  •   Pontus Gagge    16 年前
        6
  •  1
  •   JohnKeller    16 年前

    如果不评估现有的AOP框架是否能够满足您的需要,我绝不会开始尝试编写本文。HTH>

        7
  •  -1
  •   Sijin    16 年前

    既然您愿意为每个需要它的方法添加一行代码,为什么不直接从方法本身中调用MyHelper呢?

    protected string doStuffWithData(parameters...)
    {
         MyHelper.PerformCall( () => { doStuffWithDataCore(parameters...) });
    }
    
    private string doStuffWithDataCore(parameters...) {
        //Do stuff here
    }