Stef
Stef

Reputation: 325

How to assign a method obtained through reflection to a delegate? (or: how to speed up method calls through reflection)

I know there are already questions like this, but I seriously just do not understand the answers (and I can't seem to comment on them).

I am completely new to reflection, and fairly new to delegates as well, so this is quite difficult for me.

A while ago I used reflection (for the first time) to get a method, I did it like this (simplified):

object perlinObj;
MethodInfo PerlinMethod = null;    
//...
ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
perlinObj = constructor.Invoke(new object[] { });
PerlinMethod = type.GetMethod("GetValue", new Type[] { typeof(Vector3) });
//...
float PerlinFunction(Vector3 pos)
{
   return (float)((Double)PerlinMethod.Invoke(perlinObj, new object[] { pos }));
}

This works, but the problem is that it's a lot slower than just calling the method directly. So I thought, maybe I could assign it to a delegate somehow, and then call the delegate instead of using invoke, which I assume would be faster. (it is, right?)

But I cannot find how to do this. I don't understand the documentation on msdn: http://msdn.microsoft.com/en-us/library/ms228976.aspx (I'm not even sure they are dooing the same as I'm trying to do), nor do I understand what I have to do exactly from reading this: Assign method to delegate through reflection.

(And what I tried didn't work)

So can anyone explain to me what I have to do in the example code I provided?

Upvotes: 3

Views: 1473

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062540

To do this the "easy" way, you need to know the target more exactly than object; i.e. instead of:

object perlinObj;

you would need:

SomeType perlinObj;

Then, instead of storing the MethodInfo, we use Delegate.CreateDelegate to make a delegate - note I'm using int here in place of Vector3 for my convenience:

Func<SomeType, int, float> PerlinMethod;
//...
PerlinMethod = (Func<SomeType, int, float>) Delegate.CreateDelegate(
        typeof(Func<SomeType, int, float>),
        null,
        type.GetMethod("GetValue", new Type[] { typeof(int) }));
//...
float PerlinFunction(int pos)
{
    return PerlinMethod(perlinObj, pos);
}

Note that if the target instance will never change, you can simplify:

Func<int, float> PerlinMethod;
//...
PerlinMethod = (Func<int, float>) Delegate.CreateDelegate(
        typeof(Func<int, float>),
        perlinObj,
        type.GetMethod("GetValue", new Type[] { typeof(int) }));
//...
float PerlinFunction(int pos)
{
    return PerlinMethod(pos);
}

If you can't do that, then it would be necessary to use more advanced meta-programming; either ILGenerator or Expression:

Func<object, int, float> PerlinMethod;
//...
var target = Expression.Parameter(typeof(object));
var arg = Expression.Parameter(typeof(int));
var call = Expression.Call(
    Expression.Convert(target, type),
    type.GetMethod("GetValue", new Type[] { typeof(int) }),
    arg);

PerlinMethod = Expression.Lambda<Func<object,int,float>>(
    call, target, arg).Compile();

Upvotes: 3

Related Questions