views:

193

answers:

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

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

Can anyone recommend a design pattern or exact syntax that would make something like this work?

Inside the MergeFactory, I'm picturing something like this going on:

MergeFactory<Iface1, Iface2>() :
    where Iface1: IMergeable, Iface2: IMergeable
{
    IMergeable instance = Iface1Factory.CreateIface1Instance();
    instance.Merge(Iface2Factory.CreateIface2Instance());
}
+5  A: 

Sounds like a job for the 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()");
      }
   }
SwDevMan81
You should also make `Adapter<AdapteeType1, AdapteeType2>` implement both interfaces, that way an instance of the adapter can be used wherever either is accepted.
Igor Zevaka
awesome, you guys rock. thanks for clarifying the design pattern I needed to use.
djmc
+3  A: 

Check out Castle DynamicProxy, which leverages IL Emit to create a proxy object on the fly. You can Emit a proxy that implements the two interfaces and then delegates the calls to the two encapsulated instances. If you're interested, it's actually not too difficult to implement this yourself, though there are some corner cases to consider and the IL Emit classes are not particularly forgiving of errors (making them a challenge to learn.)

Dan Bryant
thanks, I talked to another friend and he recommends this as well.
djmc
+4  A: 

If you're using .NET 4.0 implement IDynamicMetaObjectProvider on a proxy class that takes instances of classes against all the interfaces in a constructor

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

Hasan Khan
sweet, I am using 4.0 on this project so this looks like a great lead. thank you very much.
djmc
+1  A: 

Hasan's answer (IDynamicMetaObjectProvider) is a good bet if you're on .NET 4.0.

You could also look into RealProxy / DynamicProxy, which have been around since .NET 1.0. I think these are how libraries like Moq fake out single interfaces at a time, and I think they also let you intercept casts, which should let you accomplish what you're looking for. Here's an article about TransparentProxy, and here's the MSDN documentation for RealProxy and RealProxy.GetTransparentProxy.

Joe White
interesting.. I kinda assumed 4.0 made c# flexible enough to do this, but I didnt know there were solutions for it since .net 1.0.. thanks
djmc
+3  A: 

As pointless as this construct might be, for some reason it intrigued me and I quickly mocked up a Castle DynamicProxy implementation for creating objects that bundles together multiple interfaces.

The mixin factory provides two methods:

object CreateMixin(params object[] objects)

Returns an object that implements any number of interfaces. In order to get to the implemented interface you must cast the returned object to that interface.

TMixin CreateMixin<TMixin, T1, T2>(T1 obj1, T2 obj2)

Returns an interface that implements the other two interfaces to achieve strong typing. That combining interface must exist at compile time.

Here are the objects:

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
}

Take note of ICatDog interface.I thought that it would be pretty cool if the dynamic proxy returns something that is strongly typed and can be used where either interface is accepted. This interface will need to be defined at compile time if strong typing is indeed desireable. Now for the factory:

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
}

Usage is best explained in these unit tests. As you can see the second method returns a type safe interface that seemlessly bundles any number of interfaces.

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

I have blogged about this in more detail and provided source and tests. You can find it here.

Igor Zevaka
hhahah damn that was pure hybrid magic. that's exactly what I was aiming for.. it might not be a very traditional style to code in for c#, but its amazing that you exposed the language's flexibility... Doctor Moreau would be proud.
djmc
Imagine what Dr Moreau could do to the monkeys with IL rewriting...
Igor Zevaka