【问题标题】:Can I create a wrapper that intercept all calls to objects implementing certain interface?我可以创建一个包装器来拦截对实现某个接口的对象的所有调用吗?
【发布时间】:2013-03-31 07:50:29
【问题描述】:

假设我有一个接口 IFoo

interface IFoo
{
  int Bar();
  int Bar2();
  void VBar();
  //etc,
}

我可以创建一个包装器来接受任何 IFoo 对象并在实际调用之前/之后执行一些操作吗?

例如当我做这样的事情时

IFoo wrappedFoo = new Wrapper<IFoo>(actualFooObject).Object;
wrappedFoo.Bar();

那么 wrapper.Bar() 方法实际上会执行类似这样的操作

PreCall(); //some code that I can define in the wrapper
actualFooObject.Bar();
PostCall();

有没有一种简单而干净的方法来做到这一点?

【问题讨论】:

  • Wrapper&lt;IFoo&gt; 对我来说没有多大意义,因为您希望能够编写 wrapper.Bar()...因此所有这些方法都必须在 Wrapper&lt;T&gt; 中定义,但它们是 @ 987654329@-特定。在我看来,您需要使IFooWrapper 实现IFoo,并对每个接口都这样做。这意味着为所有Bar 方法编写相同的Precall(); object.Bar; Postcall(),这并不简单和干净:-)
  • @caerolus 你是对的, Wrapper 没有意义。也许我们仍然可以通过为Wrapper&lt;T&gt; 定义T Object { get; } 属性来保持通用性,类似于Moq 中的mock.Object
  • @caerolus 更新了问题。谢谢!
  • 但是像IFoo wrappedFoo = new Wrapper&lt;IFoo&gt;(actualFooObject).Object; 这样你只是忽略了包装器...wrappedFoo 只会指向原始对象!

标签: c# .net methods interface


【解决方案1】:

您可以使用Code Contracts 进行此方法。查看user manual2.8 Interface Contracts 部分(pdf)。

【讨论】:

  • 你的意思是我可以在合约类中编写自定义代码,而不是定义合约?
  • 对不起,我实际上忘记了它们真的很原始。你可以尝试编写自己的合约并覆盖默认值,但无论如何这将是非常原始的。
【解决方案2】:

您可以使用 AOP。我已经使用这个库有一段时间了:

http://www.postsharp.net/products

【讨论】:

    【解决方案3】:

    如果你需要在 PreCall()PostCall 上有一些东西,简单的方法是在代理基础方法下包装

      public abstract class ProxyBase
      {
        public void Execute()
        {
          PreCondition();
          Call();
          PostCondition();
        }
        private void PreCondition()
        {
          Console.WriteLine("ProxyBase.PreCondition()");
        }
        private void PostCondition()
        {
          Console.WriteLine("ProxyBase.PreCondition()");
        }
        protected abstract void Call();
      }
      public class AppProxy<T> : ProxyBase where T : IApp
      {
        private IApp _app;
    
        public AppProxy<T> Init(IApp app)
        {
          _app = app;
          return this;
        }
    
        protected override void Call()
        {
          Console.WriteLine("AppProxy.Call()");
          _app.Call();
        }
    
        public IApp Object
        {
          get { return _app; }
        }
      }
    
      public interface IApp
      {
        void Call();
      }
    
      public interface IFoo : IApp
      {
    
      }
    
      public class ActualFoo : IApp
      {
        public void Call()
        {
          Console.WriteLine("ActualFoo.Call()");
        }
      }
    
     class Program
      {
        static void Main(string[] args)
        {
          ActualFoo actualFoo = new ActualFoo();
          var app = new AppProxy<IFoo>().Init(actualFoo);
          app.Execute();
          var o = app.Object as ActualFoo;
    
          Console.ReadLine();
    
        }
      }
    

    --------------- 输出 --------------
    ProxyBase.PreCondition()
    AppProxy.Call()
    ActualFoo.Call()
    ProxyBase.PreCondition()

    【讨论】:

    • 但是他基本上必须在他的IFoo 接口中为每个Call-like 方法编写相同的代码。这远非简单和干净
    • 另外,请注意,您不能使用app.Call 或类似方法
    【解决方案4】:

    我没有看到这样做的“干净和简单”的方式。

    我能想到的最佳选择是编写一个通用的Wrapper&lt;T&gt;,它封装了T 的实例并实现了通用的PrecallPostcall 方法:

    public class Wrapper<T>
    {
        protected T _instance;
        public Wrapper(T instance)
        {
            this._instance = instance;
        }
        protected virtual void Precall()
        {
            // do something
        }
        protected virtual void Postcall()
        {
            // do something
        }
    }
    

    这样您就可以为接口IFoo(或任何其他接口)编写自己的FooWrapper,并且只需委托方法调用:

    public class FooWrapper :Wrapper<IFoo>, IFoo
    {
        public FooWrapper(IFoo foo)
            : base(foo)
        {
        }
        public int Bar()
        {
            base.Precall(); return base._instance.Bar(); base.Postcall();
        }
        public int Bar2()
        {
            base.Precall(); return base._instance.Bar2(); base.Postcall();
        }
        public void VBar()
        {
            base.Precall();  base._instance.VBar(); base.Postcall();
        }
    }
    

    所以你可以这样使用它:

    IFoo f = new ActualFooClass();
    IFoo wf = new FooWrapper(f);
    f.Bar();
    

    当然,如果您的PrecallPostcall 方法不是通用的,那么使用Wrapper&lt;T&gt; 类确实没有意义。只需使用FooWrapper

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-18
      • 1970-01-01
      • 2020-06-24
      • 1970-01-01
      • 2011-04-29
      • 1970-01-01
      相关资源
      最近更新 更多