我能否创建一个包装器,拦截所有实现特定接口的对象的调用?

3
假设我有一个名为 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<IFoo> 对我来说没有太多意义,因为你想要能够编写 wrapper.Bar()...所有这些方法必须在 Wrapper<T> 中定义,但它们是 T 特定的。对我来说,你需要让 IFooWrapper 实现 IFoo,并为每个接口都这样做。这意味着要为所有 Bar 方法编写相同的 Precall(); object.Bar; Postcall(),这并不简单和干净 :-) - Julián Urbano
@caerolus 你说得对,Wrapper<IFoo> 没有意义。也许我们仍然可以通过为 Wrapper<T> 定义 T Object { get; } 属性来保持泛型性,类似于 Moq 中的 mock.Object - Louis Rhys
但是如果像这样做 IFoo wrappedFoo = new Wrapper<IFoo>(actualFooObject).Object;,你只是忽略了包装器... wrappedFoo 只会指向原始对象! - Julián Urbano
这个也许会有用 https://dev59.com/B3I-5IYBdhLWcg3wTWj4?rq=1 还有这个 https://dev59.com/HXRA5IYBdhLWcg3wvQhh#847975 - Julián Urbano
@caerolus 不一定,属性对象可以返回其他内容(而不是原始对象)。 - Louis Rhys
4个回答

0
如果你需要在 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()


1
但是他基本上必须为IFoo接口中的每个类似于Call的方法编写相同的代码。这远非简单和干净。 - Julián Urbano
另外,请注意您不能执行 app.Call 或类似操作。 - Julián Urbano

0
我看不到一种“简洁而简单”的方法来做这件事。
我能想到的最好的选择是编写一个通用的Wrapper<T>,它封装了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<T> 类就没有意义了。直接使用 FooWrapper 就可以了。

0

你的意思是我可以在合约类中编写自定义代码,而不是定义契约? - Louis Rhys
抱歉,我实际上忘记了它们确实非常原始。你可以尝试编写自己的合同并覆盖默认值,但无论如何这都将非常原始。 - outcoldman

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接