Click here to monitor SSC

Simon Cooper

Introduction to open instance delegates

Published Wednesday, July 28, 2010 12:15 AM

Creating a delegate to a method is normally quite a cheap operation. However, there are some reflection-based situations where you have to create a delegate dynamically using Delegate.CreateDelegate. As you can expect, such a method is many times slower than using the type-safe delegate constructor. Using open instance delegates can help alleviate this performance penalty.

For this post, I'll be using these classes in the examples:

delegate void AddDelegate(int value);

class Adder {

    public Adder(int value) {
        Value = value;
    }
    
    public int Value { get; private set; }

    public void Add(int valueToAdd) {
        int result = Value + valueToAdd;
        Console.WriteLine("{0}+{1}={2}", Value, valueToAdd, result);
    }
}

Creating a delegate

The standard way of creating a delegate to an instance method is the following (I've expanded out the syntax for clarity):

Adder add5 = new Adder(5);
AddDelegate d = new AddDelegate(add5.Add);
This simply calls the compiler-created constructor on the AddDelegate type. In situations where you need to create the delegate dynamically, you need to use the Delegate.CreateDelegate methods instead:
Adder add5 = new Adder(5);

MethodInfo addMethodInfo = typeof(Adder).GetMethod("Add");
AddDelegate d = (AddDelegate)
    Delegate.CreateDelegate(typeof(AddDelegate), add5, addMethodInfo);
In this example, in the arguments to CreateDelegate, we're specifying the type of delegate to create, the instance of Adder the method is to be run on, and the MethodInfo of the method to run as the delegate.

The hidden 'this' pointer

The reason we need to specify the firstArgument parameter to CreateDelegate is that every instance method invocation requires a reference to the object we're calling the method on (the 'this' pointer) to be the first argument it is called with, before all the declared method arguments (for Add, the value parameter). What happens if we don't specify this argument when creating the delegate?

AddDelegate d = (AddDelegate)
    Delegate.CreateDelegate(typeof(AddDelegate), null, addMethodInfo);
Not a lot it would seem; trying to invoke such a delegate throws a NullReferenceException if it's a virtual method or accesses the 'this' instance (which most instance methods do). However, things get a lot more interesting if we change the delegate signature slightly:
delegate void OpenAddDelegate(Adder explicitThis, int value);

OpenAddDelegate add = (OpenAddDelegate)
    Delegate.CreateDelegate(typeof(OpenAddDelegate), null, addMethodInfo);
Here, we're explicitly specifying the hidden 'this' pointer of the Add method as part of the delegate signature. A delegate to an instance method created in this way is called an open instance delegate, and by specifying the 'this' argument explicitly we can use this delegate instance to call the same method on multiple instances:
Adder add3 = new Adder(3);
Adder add4 = new Adder(4);

MethodInfo addMethodInfo = typeof(Adder).GetMethod("Add");
OpenAddDelegate d = (OpenAddDelegate)
    Delegate.CreateDelegate(typeof(OpenAddDelegate), null, addMethodInfo);

d(add3, 2);
d(add3, 3);
d(add4, 5);
d(add4, 6);

So?

Such a delegate may not seem immediately useful, but this can be crucial when performance is critical. Creating a delegate with CreateDelegate is an expensive operation, and if you have to call the same delegated method on many thousands of objects it can be a killer to create a new delegate instance for each object. To demonstrate this, I ran 3 tests that called a delegate of the Add method on 1,000,000 Adder objects, creating the using the normal delegate constructor, a closed CreateDelegate call for each object, and a single open instance delegate:

Delegate constructor:     0.1115875s
Closed instance delegate: 4.6705494s
Open instance delegate:   0.0621132s

Such results speak for themselves. Although rather a niche feature, when calling the same method on many thousands of separate instances, open instance delegates can improve the performance of such code many times over.

Note: This also works as expected for delegates to virtual and interface methods. You can also use the generic Func and Action delegates if you wish, rather than defining your own delegate type, as long as the type of the first argument of the delegate is compatible with the type containing the instance method you're calling.

Comments

No Comments
You need to sign in to comment on this blog

About Simon Cooper

Simon joined Red Gate as a software developer in 2007. He started off in the SQL Tools division, working on SQL Compare 7 and 8, moved onto Schema Compare for Oracle, and is now in the .NET division working on SmartAssembly.
<July 2010>
SuMoTuWeThFrSa
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
How to Kill a Company in One Step or Save it in Three
 The majority of companies that suffer a major data loss subsequently go out of business. David Wesley... Read more...

Migrating from OCS 2007 R2 to Lync: Part 4
 Having migrated the rest of our users and legacy resources across, and start getting ready to... Read more...

Automated Script-generation with Powershell and SMO
 In the first of a series of articles on automating the process of building, modifying and copying SQL... Read more...

Seth Godin: Big in the IT Business
 Seth Godin has transformed our understanding of marketing in IT. He invented the concept of 'permission... Read more...

Using SQL Test Database Unit Testing with TeamCity Continuous Integration
 With database applications, the process of test and integration can be frustratingly slow because so... Read more...