Действительно ли возможно в c# сделать фабрику, которая объединяет интерфейсы?

var mergedInstance = MergeFactory<InterfaceOne, InterfaceTwo>();

((InterfaceOne)mergedInstance).InterfaceOneMethod();
((InterfaceTwo)mergedInstance).InterfaceTwoMethod();

Кто-либо может рекомендовать шаблон разработки или точный синтаксис, который заставил бы что-то вроде этого работать?

В MergeFactory я изображаю что-то вроде этого продолжение:

MergeFactory<Iface1, Iface2>() :
    where Iface1: IMergeable, Iface2: IMergeable
{
    IMergeable instance = Iface1Factory.CreateIface1Instance();
    instance.Merge(Iface2Factory.CreateIface2Instance());
}
7
задан SwDevMan81 5 August 2010 в 03:52
поделиться

5 ответов

Какой бы бессмысленной ни была эта конструкция, по какой-то причине она меня заинтриговала, и я быстро смоделировал реализацию Castle DynamicProxy для создания объектов, объединяющих несколько интерфейсов.

Фабрика миксинов предоставляет два метода:

объект CreateMixin (params object [] objects)

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

TMixin CreateMixin (T1 obj1, T2 obj2)

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

Вот объекты:

public interface ICat {
    void Meow();
    int Age { get; set; }
}

public interface IDog {
    void Bark();
    int Weight { get; set; }
}

public interface IMouse {
    void Squeek();
}

public interface ICatDog : ICat, IDog {
}

public interface ICatDogMouse : ICat, IDog, IMouse {
}

public class Mouse : IMouse {

    #region IMouse Members

    public void Squeek() {
        Console.WriteLine("Squeek squeek");
    }

    #endregion
}

public class Cat : ICat {
    #region ICat Members

    public void Meow() {
        Console.WriteLine("Meow");
    }

    public int Age {
        get;
        set;
    }

    #endregion
}

public class Dog : IDog {
    #region IDog Members

    public void Bark() {
        Console.WriteLine("Woof");          
    }

    public int Weight {
        get;
        set;
    }

    #endregion
}

Обратите внимание на интерфейс ICatDog . Я подумал, что было бы неплохо, если бы динамический прокси-сервер возвращал что-то строго типизированное и могло использоваться там, где допустим любой интерфейс. . Этот интерфейс необходимо будет определить во время компиляции, если действительно желательна строгая типизация. Теперь о фабрике:

using Castle.DynamicProxy;

public class MixinFactory {
    /// <summary>
    /// Creates a mixin by comining all the interfaces implemented by objects array.
    /// </summary>
    /// <param name="objects">The objects to combine into one instance.</param>
    /// <returns></returns>
    public static object CreateMixin(params object[] objects) {

        ProxyGenerator generator = new ProxyGenerator();
        ProxyGenerationOptions options = new ProxyGenerationOptions();

        objects.ToList().ForEach(obj => options.AddMixinInstance(obj));

        return generator.CreateClassProxy<object>(options);
    }


    /// <summary>
    /// Creates a dynamic proxy of type TMixin. Members that called through this interface will be delegated to the first matched instance from the objects array
    /// It is up to the caller to make sure that objects parameter contains instances of all interfaces that TMixin implements
    /// </summary>
    /// <typeparam name="TMixin">The type of the mixin to return.</typeparam>
    /// <param name="objects">The objects that will be mixed in.</param>
    /// <returns>An instance of TMixin.</returns>
    public static TMixin CreateMixin<TMixin>(params object[] objects)
    where TMixin : class {
        if(objects.Any(o => o == null))
            throw new ArgumentNullException("All mixins should be non-null");

        ProxyGenerator generator = new ProxyGenerator();
        ProxyGenerationOptions options = new ProxyGenerationOptions();
        options.Selector = new MixinSelector();

        return generator.CreateInterfaceProxyWithoutTarget<TMixin>(options, objects.Select(o => new MixinInterceptor(o)).ToArray());
    }
}

public class MixinInterceptor : IInterceptor {
    private object m_Instance;

    public MixinInterceptor(object obj1) {
        this.m_Instance = obj1;
    }

    public Type ObjectType {
        get {
            return m_Instance.GetType();
        }
    }

    #region IInterceptor Members

    public void Intercept(IInvocation invocation) {
        invocation.ReturnValue = invocation.Method.Invoke(m_Instance, invocation.Arguments);
    }


    #endregion
}
public class MixinSelector : IInterceptorSelector{
    #region IInterceptorSelector Members

    public IInterceptor[] SelectInterceptors(Type type, System.Reflection.MethodInfo method, IInterceptor[] interceptors) {
        var matched = interceptors
            .OfType<MixinInterceptor>()
            .Where(mi => method.DeclaringType.IsAssignableFrom(mi.ObjectType))
            .ToArray();
        if(matched.Length == 0)
            throw new InvalidOperationException("Cannot match method " + method.Name + "on type " + method.DeclaringType.FullName + ". No interceptor for this type is defined");
        return matched;
    }

    #endregion
}

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

    [TestMethod]
    public void CreatedMixinShouldntThrow() {
        ICat obj1 = new Cat();
        IDog obj2 = new Dog();

        var actual = MixinFactory.CreateMixin(obj1, obj2);
        ((IDog)actual).Bark();
        var weight = ((IDog)actual).Weight;
        ((ICat)actual).Meow();
        var age = ((ICat)actual).Age;
    }

    [TestMethod]
    public void CreatedGeneric3MixinShouldntThrow() {
        ICat obj1 = new Cat();
        IDog obj2 = new Dog();
        IMouse obj3 = new Mouse();
        var actual = MixinFactory.CreateMixin<ICatDogMouse>(obj1, obj2, obj3);

        actual.Bark();
        var weight = actual.Weight;
        actual.Meow();
        var age = actual.Age;
        actual.Squeek();
    }

Я написал об этом более подробно, предоставил исходный код и тесты. Вы можете найти его здесь .

5
ответ дан 6 December 2019 в 09:17
поделиться

Похоже на работу для Adapter Pattern

   public partial class Form1 : Form
   {
      public Form1()
      {
         InitializeComponent();

         // Create adapter and place a request
         MergeFactoryTarget target = new Adapter<AdapteeA, AdapteeB>();
         target.InterfaceACall();
         target.InterfaceBCall();
      }
   }

   /// <summary>
   /// The 'Target' class
   /// </summary>
   public class MergeFactoryTarget
   {
      public virtual void InterfaceACall()
      {
         Console.WriteLine("Called Interface A Function()");
      }

      public virtual void InterfaceBCall()
      {
         Console.WriteLine("Called Interface B Function()");
      }
   }

   /// <summary>
   /// The 'Adapter' class
   /// </summary>
   class Adapter<AdapteeType1, AdapteeType2> : MergeFactoryTarget
      where AdapteeType1 : IAdapteeA
      where AdapteeType2 : IAdapteeB
   {
      private AdapteeType1 _adapteeA = Activator.CreateInstance<AdapteeType1>();
      private AdapteeType2 _adapteeB = Activator.CreateInstance<AdapteeType2>();

      public override void InterfaceACall()
      {
         _adapteeA.InterfaceOneMethod();
      }

      public override void InterfaceBCall()
      {
         _adapteeB.InterfaceTwoMethod();
      }
   }

   /// <summary>
   /// AdapteeA Interface
   /// </summary>
   interface IAdapteeA
   {
      void InterfaceOneMethod();
   }

   /// <summary>
   /// AdapteeB Interface
   /// </summary>
   interface IAdapteeB
   {
      void InterfaceTwoMethod();
   }

   /// <summary>
   /// The 'AdapteeA' class
   /// </summary>
   class AdapteeA : IAdapteeA
   {
      public void InterfaceOneMethod()
      {
         Console.WriteLine("Called InterfaceOneMethod()");
      }
   }

   /// <summary>
   /// The 'AdapteeB' class
   /// </summary>
   class AdapteeB : IAdapteeB
   {
      public void InterfaceTwoMethod()
      {
         Console.WriteLine("Called InterfaceTwoMethod()");
      }
   }
6
ответ дан 6 December 2019 в 09:17
поделиться

Если вы используете .NET 4.0, реализуйте IDynamicMetaObjectProvider на прокси-классе, который принимает экземпляры классов для всех интерфейсов в конструкторе

http://msdn.microsoft.com/en-us/vcsharp/ff800651.aspx

4
ответ дан 6 December 2019 в 09:17
поделиться

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

3
ответ дан 6 December 2019 в 09:17
поделиться

Ответ Хасана (IDynamicMetaObjectProvider) - хороший вариант, если вы используете .NET 4.0.

Вы также можете изучить RealProxy / DynamicProxy, которые существуют с .NET 1.0. Я думаю, что именно так библиотеки, подобные Moq , подделывают отдельные интерфейсы за раз, и я думаю, что они также позволяют вам перехватывать приведение типов, что должно позволить вам выполнить то, что вы ищете. Вот статья о TransparentProxy , а вот документация MSDN для RealProxy и RealProxy.GetTransparentProxy .

2
ответ дан 6 December 2019 в 09:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: