About Jolt.NET Libraries

Inspired by the Boost C++ libraries, Jolt.NET aims to complement the .NET Base Class Library (BCL) with algorithms, data structures, and general productivity tools. It is the hope of the authors that the features of Jolt.NET will one day be part of, or represented in the BCL and the .NET Framework.

Adaptors For Delegate Types

Recently, I’ve been working on creating adaptors for delegates of equivalent types.  Given the generic types Predicate<T> and EventHandler<TEventArgs>, I would like to create a function that transforms instances of these types into Func<T, bool> and Action<object, TEventArgs> respectively, and vice-versa.  This is a fairly straight-forward task to accomplish, but implementing a correct solution depends on what your expectations are with regards to what the resulting delegate references.

My goal is to create a new adaptor delegate that refers to the same method that is referred to by the adaptee.  When implementing the adaptor function for the Predicate<T>/Func<T, bool> case, I implemented the incorrect solution twice.  On the second attempt, I also implemented the incorrect test code, which was very negligent on my part.  It was only until other unrelated tests start failing, that I noticed my error: I had changed the semantics of the implementation and consequently the expectations of the unit tests.

Let’s look at three ways to implement a function that creates an adaptor delegate for Predicate<T>.

Option 1 : Lambda Expressions


using System;

Func<T, bool> CreateFunc<T>(Predicate<T> predicate)
{
return arg => predicate(arg);
}

This solution uses a lambda expression to create a new delegate that forwards its call to the adaptee.  The forwarding operation is implemented by the compiler as a new method on a compiler-generated type.  Clearly, this is does not achieve my goal since the method referred to by the adaptor is different from that referred to by the adaptee.

Option 2 : Delegate Constructor 

using System;

Func<T, bool> CreateFunc<T>(Predicate<T> predicate)
{
return new Func<T, bool>(predicate);
}

This solution initializes an instance of the adaptor delegate with the adaptee instance.  It looks like a copy constructor has been invoked, as the constructor accepts anything that implements the delegate’s signature.  However, don’t be deceived by the copy-constructor-like syntax of this operation.  The constructor has in fact added the adaptee to the adaptor’s invocation list.  Consequently, the method referred to by the adaptor is the Invoke() method of the adaptee.  If the constructor parameter were a method, then the resulting adaptor delegate would refer directly to the given method.

Option 3 : Delegate Factory Method

using System;

Func<T, bool> CreateFunc<T>(Predicate<T> predicate)
{
return Delegate.CreateDelegate(typeof(Func<T, bool>), predicate.Target, predicate.Method) as Func<T, bool>;
}

This solution utilizes a bit of reflection and a static factory method on the delegate class to construct the adaptor.  Notice that the factory method accepts a MethodInfo object, denoting the method that the resulting delegate will directly invoke.  Alas, we have arrived at the solution that implements my expectations.  Even though the adaptor and adaptee delegates are of different types, they both internally refer to the same method.

There is one gotcha that you should be aware of – this may be a bug with in the .NET Framework, or misstated information in the documentation.  If you refer to the documentation on the Delegate.CreateDelegate(Type, MethodInfo) method, you will notice that the method supports both static and instance methods for binding (.NET 2.0 and onwards).  However, disassembling the method in question shows that this is not the case; this overload only functions with static methods, always passing a null reference as the target object of the new delegate.  In my example above, I use another overload that accepts the target parameter to assure that both static and instance methods are adaptable.

0 comments: