【问题标题】:Is it possible in c# to make a factory that merges interfaces?是否可以在 C# 中创建一个合并接口的工厂?
【发布时间】:2010-08-05 02:16:26
【问题描述】:
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());
}

【问题讨论】:

  • 一个实现两个接口的类?
  • 嗯,是的,也许我应该进一步扩展一下我希望在 MergeFactory 中发生的事情
  • 提供有关您的动机的更多信息可能会有所帮助。我不明白为什么我想让两个独立对象的实例响应两个不同的接口,就好像它们是实现两个不同接口的同一个对象一样。
  • 是的,我猜这只是为了不同的编码风格。我正在讨论这是否可能而且我不了解自己,所以我决定把这个问题抛到 stackoverflow 上。正如你所说,你可能不想这样做,但有可能吗?

标签: c# generics design-patterns


【解决方案1】:

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

【讨论】:

  • 您还应该让Adapter&lt;AdapteeType1, AdapteeType2&gt; 实现这两个接口,这样适配器的实例就可以在任何一个被接受的地方使用。
  • 太棒了,你们摇滚。感谢您阐明我需要使用的设计模式。
【解决方案2】:

尽管这个结构可能毫无意义,但由于某种原因它引起了我的兴趣,我很快模拟了一个 Castle DynamicProxy 实现,用于创建将多个接口捆绑在一起的对象。

mixin 工厂提供了两种方法:

object CreateMixin(params object[] objects)

返回一个实现任意数量接口的对象。为了访问已实现的接口,您必须将返回的对象强制转换为该接口。

TMixin CreateMixin&lt;TMixin, T1, T2&gt;(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

【讨论】:

  • 哈哈哈该死的是纯混合魔法。这正是我的目标.. 为 c# 编写代码可能不是一种非常传统的风格,但令人惊讶的是你展示了该语言的灵活性...... Moreau 博士会感到自豪。
【解决方案3】:

如果您使用 .NET 4.0 在代理类上实现 IDynamicMetaObjectProvider,该代理类将类实例与构造函数中的所有接口相结合

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

【讨论】:

  • 甜蜜,我在这个项目上使用 4.0,所以这看起来是一个很好的线索。非常感谢。
【解决方案4】:

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

【讨论】:

  • 谢谢,我和另一个朋友谈过,他也推荐了这个。
【解决方案5】:

Hasan's answer (IDynamicMetaObjectProvider) 如果您使用的是 .NET 4.0,是一个不错的选择。

您还可以查看自 .NET 1.0 以来一直存在的 RealProxy / DynamicProxy。我认为这些是像Moq 这样的库一次伪造单个接口的方式,而且我认为它们还可以让您拦截强制转换,这应该可以让您完成所需的工作。这是an article about TransparentProxy,这是RealProxyRealProxy.GetTransparentProxy 的MSDN 文档。

【讨论】:

  • 很有趣.. 我有点假设 4.0 使 c# 足够灵活,可以做到这一点,但我不知道自 .net 1.0 以来有解决方案.. 谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-04
  • 1970-01-01
  • 2020-08-23
  • 1970-01-01
  • 1970-01-01
  • 2017-06-25
相关资源
最近更新 更多