C# Create delegate of method where parameter types

2019-09-03 15:39发布

问题:

What I am trying to do is the following (which I necessarily don't know if it's even possible); I know a run time type. I know which method I want to invoke in run time. However, I don't know this in compile time.

The GetFunction method won't be able to create a delegate of the given methodInfo since the inparam isn't of type object.

Is there a way to create a delegate of a function where I only know a Type and a MethodInfo of the method I wish to delegate?

public sealed class Functions {
    public static int SetStrValue(string s) {
        // set a string
    }

    public static int SetIntValue(int i) {
        // set an int
    }
}

public sealed class GetFunctions {
    public Func<object, int> GetFunction(Type type, MethodInfo methodInfo) {
        // what I would like to do.
        Func<object, int> func = Delegate.CreateDelegate(typeof(Func<object, int>), methodInfo);
        return t => func(t);
    }
}

public class InvokeFunctions {
    public void invokeFunction() {
        Type t = typeof(String);
        MethodInfo methodInfo = typeof(Functions).GetMethod("SetStrValue");
        int i = GetFunctions.GetFunction(t, methodInfo).Invoke("hello");
    }
}

回答1:

You can use expression trees to create a delegate. As the compile time type is not known you could try to cast the parameter to the appropriate type at runtime.

public sealed class GetFunctions
{
    public static Func<object, int> GetFunction(MethodInfo methodInfo)
    {
        var obj = Expression.Parameter(typeof (object), "obj");
        var convert = Expression.Convert(obj, methodInfo.GetParameters().First().ParameterType);
        var call = Expression.Call(methodInfo, convert);
        var lambda = Expression.Lambda<Func<object, int>>(call, obj);

        return lambda.Compile();
    }
}

public class InvokeFunctions
{
    public void invokeFunction()
    {
        MethodInfo methodInfo = typeof(Functions).GetMethod("SetStrValue");
        int i = GetFunctions.GetFunction(methodInfo).Invoke("hello");

        MethodInfo methodInfo2 = typeof(Functions).GetMethod("SetIntValue");
        int i2 = GetFunctions.GetFunction(methodInfo2).Invoke(1);
    }
}

I've removed the Type parameter and taken it directly from Method's first parameter, If that's not the intended behavior you can change it.



回答2:

You can use generics to simplify this to just:

public sealed class Functions 
{
    public static int SetValue<T>(T input) 
    {
        // set a value of type T
    }
}

And call the function with:

Functions.SetValue<string>("hello");

Or you could use a generic delegate definition if you still want to use delegates:

public delegate int MyDelegateFunction<T>(T input);