是否可以在c#中创建一个合并接口的工厂?

时间:2010-08-05 02:16:27

标签: c# generics design-patterns

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

5 个答案:

答案 0 :(得分:6)

听起来像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()");
      }
   }

答案 1 :(得分:5)

由于这个结构可能是毫无意义的,因此它引起了我的兴趣,我很快就嘲笑了Castle DynamicProxy实现,用于创建将多个接口捆绑在一起的对象。

mixin工厂提供两种方法:

object CreateMixin(params object[] objects)

返回实现任意数量接口的对象。为了进入已实现的接口,必须将返回的对象强制转换为该接口。

TMixin CreateMixin<TMixin, T1, T2>(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();
    }

我已经在博客中详细介绍了这一点,并提供了源代码和测试。你可以找到它here

答案 2 :(得分:4)

如果您正在使用.NET 4.0,则在代理类上实现IDynamicMetaObjectProvider,该代理类针对构造函数中的所有接口获取类的实例

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

答案 3 :(得分:3)

查看Castle DynamicProxy,它利用IL Emit动态创建代理对象。您可以发出实现两个接口的代理,然后将调用委托给两个封装的实例。如果您感兴趣,实际上并不是很难自己实现,尽管有一些极端情况需要考虑,而IL Emit课程并不是特别容忍错误(这使他们成为学习的挑战。)

答案 4 :(得分:2)

如果您使用的是.NET 4.0,那么

Hasan's answer(IDynamicMetaObjectProvider)是一个不错的选择。

您还可以查看自.NET 1.0以来一直存在的RealProxy / DynamicProxy。我认为这些像Moq这样的库一次假冒单个界面,我认为它们也让你拦截强制转换,这可以让你完成你想要的东西。这是an article about TransparentProxy,这是RealProxyRealProxy.GetTransparentProxy的MSDN文档。