Автоматическое создание оболочки для реализации интерфейса
У меня есть некоторые классы, которые не реализуют определенный интерфейс, но структурно соответствуют этому интерфейсу.
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 использует для проксирования своего типа.
Я не использовал его сам, поэтому не знаю, сколько усилий потребуется с вашей стороны, но я подозреваю, что это хорошая отправная точка.
Ответ 5
Взгляните на Представляем "The С# Ducktaper" - Преодоление динамического мира со статическим миром, поскольку в этом блоге описывается, что именно вы необходимо.
Ответ 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());