Expression.GetDelegateType和泛型方法

时间:2014-10-10 16:30:31

标签: c# generics reflection delegates expression-trees

我需要使用以下方法创建具有Reflection的委托:

public void Serialize<TElement>(IList<TElement> value)
{
}

这些方法的共同点是,它们返回Void并且有一个带一个泛型参数的参数。参数类型不必具体为IList<T>。在代理人的调用之前,我无法解决TElement

使用以下方法引发异常:

static Delegate CreateOpenDelegate(MethodInfo method, object target)
{
    var args = new List<Type>(method.GetParameters()
        .Select(param => param.ParameterType));
    args.Add(method.ReturnType);
    var delegateType = Expression.GetDelegateType(args.ToArray());
    return Delegate.CreateDelegate(delegateType, target, method);
}

是否有可行的替代方法在运行时创建委托,就在执行之前(一旦知道TElement并且可以在方法上调用MakeGenericMethod)?

1 个答案:

答案 0 :(得分:0)

我的理解是,只能从可以调用的MethodInfo中创建Delegate类(不要与委托关键字混淆)。如果IsGenericMethod为true且ContainsGenericParameters = true,则无法调用MethodInfo。 More Information on this.

也就是说,如果在需要创建委托时在运行时知道所有类型,则可以从它们构造一个委托。 如果出于某种原因而不是,则可能需要重组代码,以便您更紧密地创建委托。

static Delegate CreateOpenDelegate(MethodInfo method, object instance, params Type[] genericParameters)
{
    var myMethod = method.IsGenericMethod ? method.MakeGenericMethod(genericParameters) : method;
    var args = new List<Type>(myMethod.GetParameters()
        .Select(param => param.ParameterType));
    args.Add(myMethod.ReturnType);
    var delegateType = Expression.GetDelegateType(args.ToArray());
    return myMethod.CreateDelegate(delegateType, instance);
}

这是一个使用Serialize的示例,但是作为Program类上的静态实例(只是因为它很方便,如果愿意,可以将其作为实例参数来实现)。

static void Main(string[] args)
{
    var method = typeof(Program).GetMethod("Serialize");
    object myCollection = "12345".ToCharArray();
    //Get the element type for the array. In this case it is typeof(char)
    Delegate test = CreateOpenDelegate(method, null, myCollection.GetType().GetElementType());
    test.DynamicInvoke(myCollection);

    myCollection = new List<int> { 1, 2, 3, 4, 5 };
    //Get the generic argument for the collection type. In this case it is typeof(int)
    test = CreateOpenDelegate(method, null, myCollection.GetType().GetGenericArguments()[0]);
    test.DynamicInvoke(myCollection);
}

public static void Serialize<TElement>(IList<TElement> value)
{
}

收集的类型可能会根据您的情况而有所不同。我有两种不同的类型,但是也可以通过在GetType()上调用GetInterfaces()然后找到IList接口并从该接口获取通用类型来更好地抽象该类型。

您首次使用此功能时可能犯的最大错误是错误地将整个IList类型作为通用参数传递,而不是获取IList通用参数。至少我知道我仍然经常这样做。