Castle Dynamic Proxy tutorial part XIII: Mix in this, mix in that

This is part thirteen of my tutorial on Castle Dynamic Proxy.

So far we’ve covered most of basic features of Dynamic Proxy, except for one – mixins. Not getting into theoretical details, mixin is an object that stitches many other objects together, exhibiting behaviors of all these objects. Let me illustrate that in pseudo code:

var dog = Dog.New();
var cat = Cat.New();
var mixin = mixin(cat, dog);
mixin.Bark();
mixin.Meow();

In most languages this is achieved through multiple inheritance, but this is not allowed in the CLR, which imposes certain limitations on how mixins are implemented and work in Dynamic Proxy. We can’t have multiple base classes, but we can implement multiple interfaces and this is what Dynamic Proxy uses for mixins. Let’s see an example of how you would use them:

var options = new ProxyGenerationOptions();
options.AddMixinInstance(new Dictionary<string , object>());
return (Person)generator.CreateClassProxy(typeof(Person), interfaces, options);

We’re mixing class Person with Dictionary (which could be useful if you want to attach generic information store to a person). We need ProxyGenerationOptions for that. We add mixin to options using AddMixinInstance method, passing the dictionary we want to use. Next we create class proxy for Person class using the options with mixin. The Person class can by just an any ordinary, non-sealed class.

public class Person
{
	public string Name { get; set; }

	public int Age { get; set; }
}

Notice that none of its properties is virtual nor it implements any interface. That’s ok, because we don’t want to intercept any calls to Person (although we still could, obviously).

Let’s now look at the list of interfaces the proxy implements:

  System.Collections.Generic.ICollection`1[System.Collections.Generic.KeyValuePair`2[System.String,System.Object]] 
  System.Collections.Generic.IEnumerable`1[System.Collections.Generic.KeyValuePair`2[System.String,System.Object]] 
  System.Collections.IEnumerable 
  System.Collections.Generic.IDictionary`2[System.String,System.Object] 
  System.Collections.ICollection 
  System.Collections.IDictionary 
  System.Runtime.Serialization.IDeserializationCallback 
  System.Runtime.Serialization.ISerializable 
  Castle.Core.Interceptor.IProxyTargetAccessor

We have the standard IProxyTargetAccessor, and all the interfaces implemented by generic Dictionary. We can pass around our proxy as Person, but at any time cast it to any of the interfaces and use them.

static void Main(string[] args)
{
	var person = CreateProxy();
	var dictionary = person as IDictionary;
	dictionary.Add("Next Leave", DateTime.Now.AddMonths(4));
	UseSomewhereElse(person);
}

private static void UseSomewhereElse(Person person)
{
	var dictionary = person as IDictionary<string ,object>;
	var date = ((DateTime) dictionary["Next Leave"]).Date;
	Console.WriteLine("Next leave date of {0} is {1}", person.Name, date);
}

Notice that you’re casting the proxy to an interface. If you tried to cast to Dictionary<,> class, you’d get an exception. Out proxy class inherits from Person, not Dictionary<,> – that’s the limitation I talked about. What we have here is a class proxy, mixed with interface proxy with target, in one object. We obviously can mix in more than one object, but there are other limitations.

You don’t specify which mixin interfaces you want to forward to which mixin. This is implicit – if mixin implements an interface, it will be forwarded to that mixin instance, unless someone else implements it as well. You can’t have two mixins that implement the same interface. You can’t have mixin implement same interface as target, nor can you have a mixin implementing one of additional interfaces to proxy. In any of these cases when trying to create a proxy you will get an error.

<

p>This is not a very serious issue though, and I think in most cases it should be enough. In case you need more power, Dynamic Proxy v2.2 will have better support for mixin, allowing scenarios that are not possible in current version.