C#, Lambdas / Anonomous delegates

Generic Method Calls Using Expression Trees / Method Caching

The other day I wrote a bit of reflection inside an ASP .NET custom ModelBinder class to call a generic method, and my work colleague asked, isn’t that going to be slow.

I was like yeah but not that bad, besides if it proves to be a problem we can always speed it up using method caching and Expression Trees.

He then asked to see an example of that as he had not done too much with Expression Trees, I said sure I would knock one up for him.

The idea is a fairly simply one, we can to call a method, I was using Reflection which takes the Reflection hit every time, so what we need is to build up a Dynamic Delegate which we can store and which we can then call each time which will be lightning quick when compared to using Reflection, as all we are doing is calling a Delegate.

Enter ExpressionTrees.

Suppose this is my method that I want to call

//Will call this method via ExpressionTree
private T GetSomething<T>(string data)
{
    return (T)Activator.CreateInstance(typeof(T));
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

You can see that this method is generic and takes a string as a parameter. So what would an expression tree builder method look like, and what it would it return. Well it would look like this, and would return a Func delegate which we could cache somewhere, and call next time, which as I say would be very fast, as we are not doing any Reflection any more, we basically take the hit one when we 1st create the Func delegate.

//create delegate using Expression Trees
private static Func<Object, P1, T> ReflectGenericFunction<P1, T>(
    Type[] genericParams, Type objType, String methodName)
{
    ParameterExpression param = 
        Expression.Parameter(typeof(Object), "object");
    ParameterExpression param1 = 
        Expression.Parameter(typeof(P1), "paramP1");
    Expression convertedParam = 
        Expression.Convert(param, objType);
    Expression methodCall = 
        Expression.Call(convertedParam, methodName, genericParams, param1);
    LambdaExpression lambda = 
        Expression.Lambda(methodCall, param, param1);
    Expression<Func<Object, P1, T>> dynamicSetterExpression = 
        (Expression<Func<Object, P1, T>>)lambda;
    return dynamicSetterExpression.Compile();
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

And here is how we might call this method to get a Func delegate we could cache

If we know the correct generic values for the ReflectGenericFunction we can simply do this:

Type[] genericArgs = new Type[] { typeof(Person) };

//If we know the correct generic types can simply do this
Func<Object, String, Person> func = 
    ReflectGenericFunction<String, Person>(
        genericArgs, typeof(Program), "GetSomething");
var x = func(this,"Same");

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

If we need to dynamically create the types for the ReflectGenericFunction we can simply do this:

Type[] genericArgs = new Type[] { typeof(Person) };

///If we DO NOT know the correct generic types can simply do this
MethodInfo method = this.GetType().GetMethod("ReflectGenericFunction", 
    BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance);
method = method.MakeGenericMethod(new Type[] { 
    typeof(String), typeof(Person) });

Func<Object, String, Person> funcReflected = 
    (Func<Object, String, Person>)method.Invoke(
        this, new Object[] { genericArgs, 
        typeof(Program), "GetSomething" });

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

NOTE : The same thing can obviously be done using Delegate.CreateDelegate(..) but I thought this was interesting and I had to do it for my work collegue so thought I would just show you how.

Here is what you would do using Delegate.CreateDelegate

Declare a delegate type

public delegate object GetSomethingDelegate(string json);
Now create a method that will dynamically create the delegates
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

private GetSomethingDelegate GetSomethingDelegateType(Type modelType, object instance)
{
    MethodInfo method = this.GetType().GetMethod("GetSomething", 
        BindingFlags.NonPublic | BindingFlags.Instance);
    method = method.MakeGenericMethod(new Type[] { modelType });
    return (GetSomethingDelegate)Delegate.CreateDelegate(
        typeof(GetSomethingDelegate), instance, method);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

And finally call the delegate creation method

GetSomethingDelegate funcDel = GetSomethingDelegateType(typeof(Person), this);
var x3 = funcReflected(this, "Using Delegate.CreateDelegate");
Console.WriteLine(x3.Id);

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

As always here is a small demo project :

http://dl.dropbox.com/u/2600965/Blogposts/2011/08/DynamicMethodCachingUsingExpressionTrees.zip

Advertisements