Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting a Delegate into an Action<T> or Func<T> in runtime

I'm trying to improve my reflection code by creating Delegates for the Getter and Setter methods.

My code looks like this:

MyObject obj = new MyObject();
var prop = obj.GetType().GetProperty("Prop");
var getType = typeof(Func<>).MakeGenericType(prop.PropertyType);
var setType = typeof(Action<>).MakeGenericType(prop.PropertyType);

var getMethod = prop.GetGetMethod().CreateDelegate(getType, obj);
var setMethod = prop.GetSetMethod().CreateDelegate(setType, obj);

// I'd like to change this section and not to use a dynamic!!
dynamic castedGet = Convert.ChangeType(getMethod, getType);
dynamic castedSet = Convert.ChangeType(setMethod, setType);

CreateDelegate returns a Delegate and using DynamicInvoke isn't performance wise.

I casted (hardcoded) the Delegate into Action<T> \ Func<T> and saw a huge increase in my performance.

I then tried to cast the Delegate into Action<T> \ Func<T> in runtime (using Convert.ChangeType and dynamic) and my performance got hurt - probably due to the fact that I'm using a dynamic type.

I'm pretty sure that I can do this without dynamic.

I guess the solution has something to do with expression trees, but I'm not really sure how to code something like this. If someone has a good solution that doesn't use expression trees than it will be interesting to hear about it as well.

like image 368
Amir Popovich Avatar asked Sep 21 '15 14:09

Amir Popovich


1 Answers

If your objective is to be able to invoke your action/function without knowing the return type at compile time, then you probably want to end up with an Action<object> and Func<object>, right?

You can do this without having to compile an expression tree or anything, like so:

// Use reflection to create the action, invoking the method below.
var setAction = (Action<object>) this.GetType()
    .GetMethod("CastAction", BindingFlags.Static | BindingFlags.NonPublic)
    .MakeGenericMethod(prop.PropertyType)
    .Invoke(null, new object[]{setMethod});

// invoke the action like this:
object value = 42; // or any value of the right type.
setAction(value);

Using this helper method:

private static Action<object> CastAction<T>(Delegate d)
{
    var action = (Action<T>)d;
    return obj => action((T)obj);
}

My tests show this to be roughly 25% faster than using dynamic, and about 45% slower than just saying obj.Prop = 2;

like image 71
StriplingWarrior Avatar answered Sep 20 '22 01:09

StriplingWarrior