[Journal - Open Instance Delegates, Finally]

Open Instance Delegates, Finally

Monday, December 19, 2005

So I've finally figured out how to use open instance delegates, which are available in .NET 2.0 (Whidbey). For some background on this, read this post.

The basic tenet of open instance delegates is that they're bound to an instance method, but not to a specific instance. Rather, the target object is supplied at invocation time. In fact, the instance method becomes like a function with an explicit "this" parameter; the delegate type must be so declared:

public delegate void Invoker<T>(T target);

public class OpenInstancer {

    private string m_Name;

    public OpenInstancer(string sName){
        m_Name = sName;
    }

    public override string ToString(){
        return "OpenInstancer " + m_Name;
    }

    public void M(){
        Console.WriteLine("Instance method M() invoked on " + this);
    }

}

We can now create a couple of objects of the class, and use one delegate to invoke a method on either object. Creating the delegate requires that we pass a null reference for the firstArgument parameter of Delegate.CreateDelegate():

public static void Main(string[] args)
{
    OpenInstancer oi1 = new OpenInstancer("Foo");
    OpenInstancer oi2 = new OpenInstancer("Bar");

    // CreateDelegate(System.Type, object, string) won't work
    MethodInfo mi = typeof(OpenInstancer).GetMethod("Foo");

    Delegate del = Delegate.CreateDelegate(typeof(Invoker<OpenInstancer>), null, mi);
    Invoker<OpenInstancer> invoker = (Invoker<OpenInstancer>) del;
 
    invoker(oi1);
    invoker(oi2);
}

Invoking a method via a delegate is much more efficient that using MethodInfo.Invoke(). We still need to use reflection here for creating the delegate, but that's done only once.

Open instance delegates may also point to property accessors, and may be return-type-covariant and parameter-contravariant. A practical application of these features is sorting a list of objects based on arbitrary criteria, as outlined in the other post.

Have a look at Gregor.Core.Collections.CDelegatingComparer's static Create method, which creates open instance delegates for multi-level sorting. This facility is used by some of the Gregor.Core.Walk.Sort operators.

Note that the rules for parameter contravariance state that the delegate's parameter types must be equal or more derived than the method's respective parameter types. Therefore, CDelegatingComparer and its helper delegate type, ComparableGetter, are both generic types with a single type parameter for the "this" pointer.

I have some additional info on open instance and their counterpart, closed static delegates.