Saturday, March 28, 2009

Delegates in Unity - 3

Download: click here

Here I explain the guts of DelegateInterceptionExtension which is used to register delegate implementations and interceptors for those delegates.

First I needed to create a class inheriting from UnityContainerExtension. Then add the methods that the user of this extension would be using to do what the extension is supposed to do.

The main method is "RegisterDelegate" which takes the following parameters: the type of delegate to register, an implementation for it, an optional name for the implmentaion instance, and finally a list of call handlers:

public class DelegateInterceptionExtension : UnityContainerExtension
{
public DelegateInterceptionExtension RegisterDelegate(Type delegateType,
Delegate methodImpl,
string name,
ICallHandler[] handlerInstances)
{
NamedTypeBuildKey key =
new NamedTypeBuildKey(delegateType, name);
DelegateInterceptionPolicy policy =
new DelegateInterceptionPolicy(Context.Container, handlerInstances);
Context.Policies.Set(policy, key);
Context.Container.RegisterInstance(
delegateType,
methodImpl,
new ontainerControlledLifetimeManager());
return this;
}
protected override void Initialize()
{
Context.Strategies.AddNew(UnityBuildStage.Setup);
}
}


In addition to registering the given instance with Unity, this extension creates a DelegateInterceptionStrategy and a DelegateInterceptionPolicy object. The strategy object is needed for when the instance is being looked up the first time by Unity (or rather by the underlying ObjectBuilder). Since we register an instance of a delegate we set the strategy stage to Setup (or PostBuild).

The policy object is a helper object that is later used by the strategy. The policy carries the call handlers for the given registration.

The strategy class inherits from BuilderStrategy and implements the method PostBuildUp():

public class DelgateInterceptionStrategy : BuilderStrategy
{
public override void PostBuildUp(IBuilderContext context)
{
Type theType = BuildKey.GetType(context.OriginalBuildKey);
DelegateInterceptionPolicy policy =
context.Policies.Get<DelegateInterceptionPolicy>(context.OriginalBuildKey, false);
if (policy != null && policy.GetHandlerCount() > 0)
{
HandlerPipeline pipeline =
new HandlerPipeline(policy.GetHandlers());
DelegateInterceptor interceptor =
new DelegateInterceptor(pipeline, (Delegate)context.Existing);
context.Existing = interceptor.GetInterceptingMethod(theType);
}
}
}


When PostBuildUp is called, it checks to see if there are call handlers. If not, there is nothing to do and it lets the container return the initially registered instance.

If any call handlers were speficied for the resolved type (or type and instance name) then it creates an instance of DelegateInterceptor. This interceptor has a "dynamic" method that provides a new implementation for the requested delegate. In place of the originally registered implementation, the intercepting implementation is returned to caller. This dynamic implementation has the same signature as the delegate itself.

The DelegateInterceptor class, takes the original target and a list pipeline object (created from the call handlers). The pipeline object is taken from the Unity interception extension:

public class DelegateInterceptor
{
private HandlerPipeline pipeline;
private Delegate target;

public DelegateInterceptor(HandlerPipeline pipeline, Delegate target)
{
this.pipeline = pipeline;
this.target = target;
}
...
}


The interceptor class needs to ultimately invoke the target instance (the originally registered instance) once all call handlers in the pipeline are called. This process is listed below and is pretty much taken from the source code for Unity:

public object InterceptingMethodBody(object[] targetArgs)
{
IMethodReturn result = pipeline.Invoke(
new VirtualMethodInvocation(null, target.Method, targetArgs),
(IMethodInvocation inputs, GetNextHandlerDelegate getNext) =>
{
try
{
// this is where we call the original target
object returnValue =
target.Method.Invoke(target.Target, targetArgs);
if(target.Method.ReturnType == typeof(void))
{
return inputs.CreateMethodReturn(null);
}
return inputs.CreateMethodReturn(returnValue);
}
catch (Exception ex)
{
return inputs.CreateExceptionMethodReturn(ex);
}
});

if (result.Exception != null)
throw result.Exception;
return result.ReturnValue;
}


As you can see from its signature, this method takes an array of objects and passes them along to the call chain. These arguments are supplied by the user to the called delegate and they are entirely based on the registered delegate.

The above method cannot be passed to the user yet since it does not comply with the signature of the registered delegate (unless the delegate itself happens to take an object[] as the only parameter). So an impersonator, with the same signature as the delegate, is needed to take the call parameters, stack them in the form of an object[], and pass them to the interceptor.

For that we need the help of IL generation. DelegateInterceptor has a method to do that:

public Delegate SubstituteDelegate(Type typeToSubstitute)
{
MethodInfo interceptorInfo = this.GetType().GetMethod("InterceptingMethodBody");
MethodInfo methodInfo = typeToSubstitute.GetMethod("Invoke");
ParameterInfo[] paramInfos = methodInfo.GetParameters();
Type[] types = new Type[paramInfos.Length + 1];
// the very first argument to the new method will be the enclosed object
types[0] = this.GetType();
for (int i = 0; i < paramInfos.Length; i++)
{
types[i+1] = paramInfos[i].ParameterType;
}
DynamicMethod dm = new DynamicMethod(typeToSubstitute.ToString() + "Substitute",
methodInfo.ReturnType,
types,
this.GetType());
ILGenerator il = dm.GetILGenerator();
// emit: object[] arr = new object[paramInfos.Length];
il.DeclareLocal(typeof(Object[]));
il.Emit(OpCodes.Ldc_I4, paramInfos.Length);
il.Emit(OpCodes.Newarr, typeof(Object));
il.Emit(OpCodes.Stloc_0);
// emit: arr[i] = arg_i+1;
for (int i = 0; i < paramInfos.Length; i++)
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldarg, i+1);
if (paramInfos[i].ParameterType.IsValueType)
{
il.Emit(OpCodes.Box, paramInfos[i].ParameterType);
}
il.Emit(OpCodes.Stelem_Ref);
}
// emit: interceptorInfo.Invoke(obj, arr);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Call, interceptorInfo);
// emit: return // void
if (methodInfo.ReturnType == typeof(void))
{
il.Emit(OpCodes.Pop);
}
// emit: return (methodInfo.ReturnTpe)result
else
{
if (methodInfo.ReturnType.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, methodInfo.ReturnType);
}
}
il.Emit(OpCodes.Ret);
return dm.CreateDelegate(typeToSubstitute, this);
}


All this method does is create a delegate implementation. This helper implementation takes the passed arguments (i.e. method(arg0, arg1, arg2)) and passes them to "InterceptingMethodBody" method of the interceptor.

In the supplied code, I have provided a whole bunch of test cases to ensure this part of the code is behaving properly.

I'm sure there are a lot complexities that I still have not considered here but I'm hoping that this would provide you with enough base to go on if you find the delegate interception concept useful.

I hope you benfited form this post. Comments are appreciated.

Delegates in Unity - 2

Download: click here

I decided to create my own interception implemented as a UnityContainerExtension. In this post, I'll show how this extension is used to register and intercept delegates. The internals of this extension are discussed in the next post.

To activate the extension, use the following:

container = new UnityContainer();
container.AddNewExtension<DelegateInterceptionExtension>();


Then create your handlers. Here I could have comeup with my own Handler signatures but I decided to use the exisiting ICallHandler from Unity interception. If you're handlers need to be "builtup" then register them with the container. Otherwise, just create new instances and pass those along. If you register them with the container, you can just pass their "alises":

callCounter = new CounterCallHandler();
container.RegisterInstance<ICallHandler>("callCounter", callCounter);


Now you're ready to register your interception. For that I decided to provide one single easy step which registers the delegate instance and also adds its call handlers:

MethodImpl impl = new MethodImpl();
container.Configure<DelegateInterceptionExtension>()
.RegisterDelegate<VoidMethodIntParam>(
impl.VoidMethodIntParamImpl, null, new string[] { "callCounter" }
);


You can now lookup the delegate and call it:

VoidMethodIntParam d = container.Resolve();
d();


My call handler increments a counter everytime it is called. So to show that in fact my call handler was called I assert that the call count is 1:

Assert.AreEqual(1, callCounter.Count);


The implementation of your delegate can be anything from an instance method to a static method to a virtual method to an anonymous method.

When creating anonymous delegates, you can go further and enclose objects from a higher scope in it. In other terms you can create a "closure" over those objects:

container.Configure<DelegateInterceptionExtension>()
.RegisterDelegate<VoidMethod>(
delegate()
{
// here I create a closure:
callCounter.Count += 1;
Trace.WriteLine("Anonymous delegate was called");
},
null,
new string[] { "callCounter" });
VoidMethod d = container.Resolve<VoidMethod>();
d();
Assert.AreEqual(2, callCounter.Count);

Delegates in Unity - 1

The thought originally came to me when I encountered the stack overflow exception during the dependency resolution of inter-dependent objects in Unity (or any other service-lookup framework).

If you're not familiar with this problem, try creating two or more classes that require each other's services: for instance an AccountService and a LoanService. Then try to inject the dependencies at runtime using a dependency-injection framework like Unity. The iterative nature of this sort of lookup causes a stack overflow.

There are ways to avoid this problem which i won't be talking about in this post. However, what I will be talking about is reducing the scope of a service to the point of it being a single method, a delegate.

Before reading further and if you haven't, take a look at the differences between interfaces and delegates in this MSDN article. (if the link is stale, search for delegate instead of interfaces in MSDN):
http://msdn.microsoft.com/en-us/library/ms173173.aspx

If I define my services in terms of delegates, I can implement them in any grouping that I want: I can implement each delegate in a dedicated class, bundle all into one class, or try to logically group them in corresponding classes.
You can go from coarse-grained (all ine one class) to the finest-grained grouping (each delegate in its own class) of program logic.

Let's get started. First I tried Unity as is and susprisingly it handled delegates with no problem.

To register delegate implementations, register them as "instances". Here's an example:
1. Declare the delegate

public delegate void VoidMethod();

2. Implement it somewhere:

public class MethodImpl
{
public void VoidMethodImpl()
{
MarkAsCalled("VoidMethodImpl");
Trace.WriteLine("VoidMethodImpl was called");
}
}

3. Register the implementation for the delegate type:

DelegateTestSupport.MethodImpl impl =
new DelegateTestSupport.MethodImpl();
container.RegisterInstance<VoidMethod>(impl.VoidMethodImpl);

4. Try to resolve the delegate and call it:

container.Resolve<VoidMethod>()();

It works perfectly. It makes you gain a new sense of appreciation for the language and also the work put in Unity.

You could also implement the delegates on the fly, using anonymous delegates or Lambda expressions:

container.RegisterInstance<VoidMethod>(
delegate()
{
Debug.WriteLine("anonymous delegate was called");
}
);



So the next optional step is to try to intercept calls to your delegates using Unity Interception extension. You'd probably have some common, cross-cutting concerns that you have encapsulated and now ready to apply to qualified delegates.

Here's where I got stopped. None of the interceptors in Unity could intercept a call to delegates registered like above. At least I could'nt get them to work.

So how would one go about intercepting delegates registered in a container? That's discussed in the next post.