Автоматическое создание оболочки для реализации интерфейса - PullRequest
16 голосов
/ 17 декабря 2009

У меня есть некоторые классы, которые не реализуют определенный интерфейс, но структурно соответствуют этому интерфейсу.

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, но я никогда не использовал это, и на первый взгляд это выглядит не очень легко.

Есть ли более простой способ или, возможно, есть библиотека, которая уже реализует это?

Ответы [ 8 ]

8 голосов
/ 17 декабря 2009

То, что вы пытаетесь выполнить, называется печатью утки. Есть несколько специализированных библиотек, которые позволят вам сделать это, хотя я не использовал ни одну из них.

Без особых усилий (и некоторых размышлений) вы можете использовать Castle Dynamic Proxy для этого, используя подход, изложенный здесь .

Если по какой-то причине подход, основанный на перехватчиках, был бы неприемлем для вас, Dynamic Proxy не поддерживает его «из коробки» (пока), но если вы используете бета-версию 2.2, было бы довольно легко это реализовать в типизированный способ (без использования перехватчиков), предоставляя свой собственный построитель прокси-типов (посмотрите, как реализованы миксины).

6 голосов
/ 17 декабря 2009

Если вам нужна легкая и простая поддержка Duck Typing, вы также можете проверить: Duck Typing project . Работает с .Net 2.0 и новее .

Пример использования (взято с сайта Дэвида Мейера ):

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);
    }
}

Используя методы расширения, вы могли бы упростить его до чего-то вроде этого (например, синтаксис Барт Де Смета

)
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 голосов
/ 17 декабря 2009

Вы можете создать новый класс

class SubBar : IFoo, Bar {
}

и создайте для него экземпляр (при условии, что Bar действительно имеет тип утки, то есть точные IFoo методы).

2 голосов
/ 17 декабря 2009

Возможно, вы захотите взглянуть на Castle Project DynamicProxy . Вот что Rhino.Mocks использует для проксирования своего типа.

Я сам этим не пользовался, поэтому не знаю, сколько потребуется усилий с вашей стороны, но я подозреваю, что это хорошая отправная точка.

1 голос
/ 17 декабря 2009

Взгляните на Представляем «Ducktaper C #» - соединяя динамический мир со статическим миром , поскольку в этом блоге описывается именно то, что вам нужно.

0 голосов
/ 17 декабря 2009

Вот немного другой подход с использованием дженериков. Это потребует немного больше работы. Вам нужно реализовать оболочку для каждого интерфейса и вызывать все методы с отражением.

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());
0 голосов
/ 17 декабря 2009

Хотя я не использовал их сам, я думаю, что шаблоны T4 в Visual Studio можно использовать для генерации кода динамических типов текст ссылки .

0 голосов
/ 17 декабря 2009

Если вы хотите использовать .NET 4, решение может состоять в том, чтобы определить класс-оболочку как DynamicObject и преобразовать вызовы динамических методов в вызовы обернутого класса с помощью отражения ( Во всяком случае, я не уверен, что это будет на самом деле меньше работы, а также учитывать возможные проблемы производительности, связанные с использованием отражения).

...