【问题标题】:Automatically creating a wrapper to implement an interface自动创建包装器来实现接口
【发布时间】:2010-12-27 13:45:40
【问题描述】:

我有一些类没有实现某个接口,但在结构上符合那个接口。

interface IFoo {
    void method();
}

class Bar {  // does not implement IFoo
   public void method() {...}
}

现在,我可以为那些简单地委托给被包装类的类编写一个包装器

class BarWrapper : IFoo {
   Bar bar = new Bar();
   public void method()
   {
      bar.method();
   }
}

但这是很多乏味的工作。这些包装类可以以某种方式自动生成吗?比如:

IFoo foo = CreateWrapper<IFoo>(new Bar());

我相信您可以使用 Reflection.Emit 来做到这一点,但我从未使用过它,而且乍一看并不容易。

有没有更简单的方法,或者是否有一个库已经实现了这个?

【问题讨论】:

标签: c# wrapper


【解决方案1】:

您要完成的工作称为鸭式打字。 有一些专用的库可以让你做到这一点,虽然我没有使用过它们。

您可以使用 Castle Dynamic Proxy 做到这一点,只需一点努力(和一些思考),使用概述的方法 here

如果由于某种原因基于拦截器的方法对您来说是不可接受的,动态代理不支持开箱即用(目前),但如果您使用 2.2 版测试版,则很容易以强大的方式提供它类型化方式(不使用拦截器),通过提供您自己的代理类型构建器(看看 mixins 是如何实现的)。

【讨论】:

    【解决方案2】:

    如果您想要轻松简单的 Duck 输入支持,您也可以查看:Duck Typing project。它适用于 .Net 2.0 和更新版本

    使用示例(取自David Meyer's site):

    public interface ICanAdd
    {
        int Add(int x, int y);
    }
    
    // Note that MyAdder does NOT implement ICanAdd, 
    // but it does define an Add method like the one in ICanAdd:
    public class MyAdder
    {
        public int Add(int x, int y)
        {
            return x + y;
        }
    }
    
    public class Program
    {
        void Main()
        {
            MyAdder myAdder = new MyAdder();
    
            // Even though ICanAdd is not implemented by MyAdder, 
            // we can duck cast it because it implements all the members:
            ICanAdd adder = DuckTyping.Cast<ICanAdd>(myAdder);
    
            // Now we can call adder as you would any ICanAdd object.
            // Transparently, this call is being forwarded to myAdder.
            int sum = adder.Add(2, 2);
        }
    }
    

    使用扩展方法,您可以将其简化为类似这样的内容(类似于Bart De Smet's 语法)

    MyAdder myAdder = new MyAdder(); // this doesn't implement the interface
    ICanAdd adder = myAdder.AsIf<ICanAdd>(); // but it quacks like a duck
    int sum = adder.Add(2, 2);
    

    【讨论】:

      【解决方案3】:

      你可以新建一个类

      class SubBar : IFoo, Bar {
      }
      

      并实例化它(假设 Bar 确实具有鸭子类型,即确切的 IFoo 方法)。

      【讨论】:

        【解决方案4】:

        您可能想查看 Castle Project 的 DynamicProxy。这就是 Rhino.Mocks 用于其类型代理的内容。

        我自己没有使用过,所以我不知道你需要付出多少努力,但我怀疑这是一个很好的起点。

        【讨论】:

        • AFAIK,当前版本的动态代理只对接口和具体类型起作用,只能拦截虚方法。它不做映射。但是,它可能会在未来解决这个问题:using.castleproject.org/display/CASTLE/…(向下滚动到“类型包装”)
        • @Mark:这个问题只提到了使用接口,所以这听起来对我来说不是一个相关的重大限制。
        • @Jon Skeet:是的,但是他想包装/代理一个既不实现接口也没有虚方法的具体类,所以我认为它非常相关。
        • @Mark:但他只需要拦截接口调用——我想他可以很容易地让那些调用特定实例上的非虚拟方法。它不会是单行的,但它是一个起点。也许我错过了重点,但如果它适用于模拟,我看不出它是如何无法为此工作的......
        • @Jon:我也设法画了 Krzysztof Koźmic 来回答这个问题。我会让他在这里做最后的决定——他比我更了解动态代理:)
        【解决方案5】:

        看看Introducing “The C# Ducktaper” – Bridging the dynamic world with the static world,因为这篇博文准确描述了您的需求。

        【讨论】:

          【解决方案6】:

          如果您愿意使用 .NET 4,一个解决方案可能是将包装类定义为 DynamicObject,并使用反射将对动态方法的调用转换为对包装类的调用(我不是确定这是否实际上会减少工作量;还要考虑与使用反射相关的可能的性能问题)。

          【讨论】:

            【解决方案7】:

            虽然我自己没有使用过它们,但我认为 Visual Studio 中的 T4 模板可以用于动态类型 link text 的代码生成。

            【讨论】:

              【解决方案8】:

              这是使用泛型的一种稍微不同的方法。这需要更多的工作思考。您需要为每个接口实现一个包装器,并通过反射调用所有方法。

              class FooWrapper<T> : IFoo
              {
                 private T obj;
              
                 public FooWrapper(T obj)
                 {
                    this.obj = obj;
                 }
              
                 public void method()
                 {
                    // call method with reflection
                    // obj.method();
                 }
              }
              
              IFoo foo = new FooWrapper(new Bar());
              

              【讨论】:

              • +1: 也会这样做。加上添加一些反射缓存,以最大限度地减少通过反射查找方法所增加的性能损失
              • 好主意,但如果您使用反射,那么您不妨选择一些现有的鸭子打字解决方案。最后,出于性能原因,您最终会发出 IL 代码并重新发明轮子。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2017-04-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多