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

This is part thir­teen of my tuto­r­ial on Cas­tle Dynamic Proxy.

So far we've cov­ered most of basic fea­tures of Dynamic Proxy, except for one – mix­ins. Not get­ting into the­o­ret­i­cal details, mixin is an object that stitches many other objects together, exhibit­ing behav­iors of all these objects. Let me illus­trate that in pseudo code:

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

In most lan­guages this is achieved through mul­ti­ple inher­i­tance, but this is not allowed in the CLR, which imposes cer­tain lim­i­ta­tions on how mix­ins are imple­mented and work in Dynamic Proxy. We can’t have mul­ti­ple base classes, but we can imple­ment mul­ti­ple inter­faces and this is what Dynamic Proxy uses for mix­ins. Let’s see an exam­ple 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 mix­ing class Per­son with Dic­tio­nary (which could be use­ful if you want to attach generic infor­ma­tion store to a per­son). We need Prox­y­Gen­er­a­tionOp­tions for that. We add mixin to options using AddMix­inIn­stance method, pass­ing the dic­tio­nary we want to use. Next we cre­ate class proxy for Per­son class using the options with mixin. The Per­son class can by just an any ordi­nary, non-sealed class.

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

	public int Age { get; set; }
}

Notice that none of its prop­er­ties is vir­tual nor it imple­ments any inter­face. That’s ok, because we don’t want to inter­cept any calls to Per­son (although we still could, obviously).

Let’s now look at the list of inter­faces 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 stan­dard IProx­y­Tar­ge­tAc­ces­sor, and all the inter­faces imple­mented by generic Dic­tio­nary. We can pass around our proxy as Per­son, but at any time cast it to any of the inter­faces 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 cast­ing the proxy to an inter­face. If you tried to cast to Dic­tio­nary<,> class, you’d get an excep­tion. Out proxy class inher­its from Per­son, not Dic­tio­nary<,> – that’s the lim­i­ta­tion I talked about. What we have here is a class proxy, mixed with inter­face proxy with tar­get, in one object. We obvi­ously can mix in more than one object, but there are other limitations.

You don’t spec­ify which mixin inter­faces you want to for­ward to which mixin. This is implicit – if mixin imple­ments an inter­face, it will be for­warded to that mixin instance, unless some­one else imple­ments it as well. You can’t have two mix­ins that imple­ment the same inter­face. You can’t have mixin imple­ment same inter­face as tar­get, nor can you have a mixin imple­ment­ing one of addi­tional inter­faces to proxy. In any of these cases when try­ing to cre­ate a proxy you will get an error.

This is not a very seri­ous issue though, and I think in most cases it should be enough. In case you need more power, Dynamic Proxy v2.2 will have bet­ter sup­port for mixin, allow­ing sce­nar­ios that are not pos­si­ble in cur­rent version.

Tech­no­rati Tags: ,

Comments are closed.