Автоматическое создание оболочки для реализации интерфейса

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

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

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

Ответы

Ответ 1

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

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

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

Ответ 2

Если вам нужна легкая и простая поддержка Duck typing, вы также можете проверить: Проект Duck Typing. Он работает с .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);
    }
}

Используя методы расширения, вы можете упростить его во что-то вроде этого (simlar to синтаксис Bart De Smet)

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 DynamicProxy. Это то, что Rhino.Mocks использует для проксирования своего типа.

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

Ответ 6

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

Ответ 7

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

Ответ 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());