2009-11-03 2 views
5

Я смотрю на то, как решить проблему, и я даже не уверен, что это может быть вообще возможно в C# & .NET 3.5:C# Как сгенерировать объект, реализующий разные интерфейсы динамически во время выполнения?

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

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

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

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

+0

Во-первых, благодарю вас всех за ваши комментарии и предложения. Очень ценим! Хорошо, мне кажется, я должен немного подробнее остановиться на проблеме, с которой я столкнулся. У меня есть определенное количество возможностей, например. CanMakeCoffe, CanCleanKitchen и т. Д. Есть некоторые возможности, которые будут реализованы большинством устройств, например. CanMakeCoffee. То, что я хотел бы избежать, чтобы определить где-то в классах исходного кода для этих устройств, как в CDeviceOne: ICanMakeCoffee, ICanCleanKitchen CDeviceTwo: ICanMakeCoffee –

+0

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

ответ

3

Попробуйте что-нибудь наподобие LinFu.DynamicObject.

+0

Очень интересно. Мне особенно нравится идея создания mixin. Я просто немного не решаюсь использовать такую ​​структуру для производственного кода ... Мне придется посмотреть на лицензию. В любом случае спасибо за это! –

+0

Лицензируется как LGPL. И вы должны поблагодарить Филиппа, а не меня :) –

5

Используйте mocking framework такие как Moq, RhinoMocks или TypeMock Isolator

Если вы хотите сделать что-то более низкий уровень, такие вещи, как Castle DynamicProxy может быть хорошим направлением для вас.

+2

Поиск утиной печати также может дать результаты. – leppie

+0

@leppie: Очень хорошая точка. @Timo: Если вы используете динамический язык, такой как IronPython или IronRuby (или связанные с динамикой вещи на C# 4), вы могли бы достичь чего-то полезного (хотя вы не использовали бы настоящие 'интерфейс' defintions и' is' или 'as 'в реализации –

+0

@leppie: спасибо за ваше предложение. Я уже посмотрел на утиную печать, но я не был уверен, поможет ли она мне в этой проблеме. Я все равно посмотрю! @Ruben: спасибо за ваши указатели на DynamicProxy. Наиболее интересно! Я также поближе посмотрю туда. –

0

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

Для заинтересованных, я сделал это некоторое время назад, детали немного туманны.

+2

Возможно, было бы так же легко выбросить il-код в динамическую сборку в памяти. Тем не менее, насмешливая структура может сделать это проще/меньше кода. ;) – sisve

1

Возможно, вам не нужно делать это так «динамичным».

Вы проверили шаблон Abstract Factory? Похоже, что в основном вам нужно создать конкретную реализацию для каждого из ваших интерфейсов на основе устройства типа.

Вам не нужно иметь один класс, реализующий множество интерфейсов, достаточно иметь соответствующую реализацию конкретного интерфейса, когда ваш код запрашивает его.

Каждая конкретная реализация вашего абстрактного завода может генерировать несколько реализаций интерфейса на основе вашего типа устройства.

Пример:

public interface IDeviceFactory 
{ 
     ISomething GetSomeInterface(); 
     ISomethingElse GetSomeOtherInterface(); 
} 

, а затем вы реализуете конкретный завод для каждого устройства:

public class SimpleDeviceFactory : IDeviceFactory 
{ 
    public virtual ISomething GetSomeInterface() 
    { return Something.Empty; } 

    public virtual ISomethingElse GetSomeOtherInterface() 
    { return new SomeSimpleConreteImplementation(); } 
} 

или, может быть:

public class ComplexDeviceFactory : IDeviceFactory 
{ 
    public virtual ISomething GetSomeInterface() 
    { return new ComplexStuff(); } 

    public virtual ISomethingElse GetSomeOtherInterface() 
    { return new EvenMoreComplexStuff(); } 
} 

И вот, наконец, вы создаете право на заводе для вашего устройства:

public class DeviceFactory 
{ 
    public static IDeviceFactory CreateForDevice(IDevice device) 
    { 
      DeviceType type = device.Type; // or something like this 
      switch (type) 
      { 
       case DeviceType.Simple: 
       return new SimpleDeviceFactory(); 

       case DeviceType.Complex: 
       return new ComplexDeviceFactory(); 

       default: 
       throw new NotImplementedException(); 
      } 
    } 
} 

Обратите внимание, что я также отметил реализацию метода IDeviceFactory как virtual, так что вы можете легко повторно использовать или переопределять определенные интерфейсы для определенного устройства.

+0

Спасибо Groo, это действительно простой подход к проблеме! Как я уже сказал в своем комментарии к моему первоначальному вопросу, я хотел бы избежать сделать код «ориентированным на устройство» (создавать классы для каждого возможного устройства) и, скорее, создать объект на основе обратной связи с устройством (что он способен делать). –

0

.NET 4 может упростить работу, вы можете использовать одну из уже упомянутых фреймворков или перейти по пути System.Reflection. Вы найдете множество образцов в Интернете.

Я делал оба в прошлом. Роллинг моего собственного il.Удалить вещи и использовать фреймворк (Spring.NET в моем случае). Для всего, кроме тривиальных вещей, используйте одну из фреймворков.

0

Вы также можете попробовать с проектом Re-mix. Одной из основных особенностей этого проекта является создание mixins, что позволяет добавлять интерфейсы с реализациями и подчиняться другим классам. Взгляните этот пример:

using Remotion.Mixins; 
using Remotion.TypePipe; 
//... 

public interface ITargetInterface 
{ 
    void DoSomething(); 
} 
// . . . 
public class TargetImplementation : ITargetInterface 
{ 
    public void DoSomething() 
    { 
     Console.WriteLine("ITargetInterface.DoSomething()"); 
    } 
} 
// . . . 
public interface IMixinInterfaceA 
{ 
    void MethodA(); 
} 
// . . . 
public class MixinImplementationA : IMixinInterfaceA 
{ 
    public void MethodA() 
    { 
     Console.WriteLine("IMixinInterfaceA.MethodA()"); 
    } 
} 
// . . . 
public interface IMixinInterfaceB 
{ 
    void MethodB(int parameter); 
} 

// . . . 
public class MixinImplementationB : IMixinInterfaceB 
{ 
    public void MethodB(int parameter) 
    { 
     Console.WriteLine("IMixinInterfaceB.MethodB({0})", parameter); 
    } 
} 

Затем вы можете объединить эти типы создать подмешать:

var config = MixinConfiguration.BuildFromActive() 
      .ForClass<TargetImplementation>() 
      .AddMixin<MixinImplementationA>() 
      .AddMixin<MixinImplementationB>() 
      .BuildConfiguration(); 
MixinConfiguration.SetActiveConfiguration(config); 

К сожалению, вы не можете просто вызвать новое на TargetImplementation и ожидают подмешать. Вместо этого вы должны попросить Re-mix создать экземпляр TargetImplementation, чтобы он мог создать новый тип для вашей спецификации и создать его экземпляр. Когда запрашивается экземпляр TargetImplementation, он будет возвращать mixin, содержащий все интерфейсы и классы в сочетании.

ITargetInterface target = ObjectFactory.Create<TargetImplementation>(ParamList.Empty); 

target.DoSomething(); 

var targetAsMixinA = target as IMixinInterfaceA; 
if (targetAsMixinA != null) 
{ 
    targetAsMixinA.MethodA(); 
} 

var targetAsMixinB = target as IMixinInterfaceB; 
if (targetAsMixinB != null) 
{ 
    targetAsMixinB.MethodB(30); 
} 
Смежные вопросы