简体   繁体   English

如何通过反射调用带参数的泛型方法?

[英]How do you call a generic method with out parameters by reflection?

Suppose I have a class like this, containing a generic method with an out parameter: 假设我有一个这样的类,包含带out参数的泛型方法:

public class C
{
    public static void M<T>(IEnumerable<T> sequence, out T result)
    {
        Console.WriteLine("Test");
        result = default(T);
    }
}

From reading the answers to a couple of other questions ( How to use reflection to call generic Method? and Reflection on a static overloaded method using an out parameter ), I thought I might be able to invoke the method via reflection as follows: 从阅读答案到其他几个问题( 如何使用反射调用泛型方法?使用out参数反映静态重载方法 ),我想我可以通过反射调用方法,如下所示:

// get the method
var types = new[] { typeof(IEnumerable<int>), typeof(int).MakeByRefType() };
MethodInfo mi = typeof(C).GetMethod(
    "M", BindingFlags.Static, Type.DefaultBinder, types, null);

// convert it to a generic method
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) });

// call it
var parameters = new object[] { new[] { 1 }, null };
generic.Invoke(null, parameters);

But mi is coming back null. 但是mi回来了。 I've tried using object instead of int in the types array but that doesn't work either. 我尝试在types数组中使用object而不是int ,但这也不起作用。

How can I specify the types (needed for the out parameter) for a generic method before the call to MakeGenericMethod ? 在调用MakeGenericMethod 之前 ,如何为泛型方法指定类型(out参数所需)?

You've passed parameters that will find M<T>(IEnumerable<int>, ref int) . 您已经传递了将找到M<T>(IEnumerable<int>, ref int)
You need to find M(IEnumerable<T>, ref T) (the distinction between ref and out exists only in the C# language; reflection only has ref ). 你需要找到M(IEnumerable<T>, ref T)refout之间的区别只存在于C#语言中;反射只有ref )。

I'm not sure how to pass that; 我不知道如何通过; you may need to loop through all methods to find it. 您可能需要遍历所有方法才能找到它。

On an unrelated note, you need to pass more BindingFlags : 在不相关的说明中,您需要传递更多BindingFlags

BindingFlags.Public | BindingFlags.Static

This will let you call the method: 这将让你调用方法:

MethodInfo mi = typeof(C).GetMethod("M");
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) });
var parameters = new object[] { new[]{1},null};
generic.Invoke(null, parameters);

And to get the out parameter: 并获得out参数:

Console.WriteLine((int)parameters[1]); //will get you 0(default(int)).

I'm still interested to know what the syntax is for specifying an array of template types, or if it's not possible 我仍然有兴趣知道指定模板类型数组的语法是什么,或者如果不可能的话

I don't think it's possible to pass that kind of detailed type specification to GetMethod[s] . 我认为不可能将这种详细的类型规范传递给GetMethod[s] I think if you have a number of such M s to look through, you have to get them all and then filter by the various properties of the MethodInfo s and contained objects, eg as much of this as is necessary in your particular case: 我想如果你有很多这样的M要查看,你必须得到它们然后按MethodInfo的各种属性和包含的对象进行过滤,例如在你的特定情况下需要的大部分内容:

var myMethodM =
    // Get all the M methods
    from mi in typeof(C).GetMethods()
    where mi.Name == "M"

    // that are generic with one type parameter
    where mi.IsGenericMethod
    where mi.GetGenericArguments().Length == 1
    let methodTypeParameter = mi.GetGenericArguments()[0]

    // that have two formal parameters
    let ps = mi.GetParameters()
    where ps.Length == 2

    // the first of which is IEnumerable<the method type parameter>
    where ps[0].ParameterType.IsGenericType
    where ps[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
    where ps[0].ParameterType.GetGenericArguments()[0] == methodTypeParameter

    // the second of which is ref <the method type parameter>
    where ps[1].ParameterType.IsByRef
    where ps[1].ParameterType.GetElementType() == methodTypeParameter

    select mi;

This is a well known-problem; 这是一个众所周知的问题; to find the method, you need to know its type parameter, but you can't know its type parameter without knowing the method first... 要找到该方法,您需要知道它的类型参数,但是如果不先了解方法就无法知道它的类型参数...

An obvious but inelegant solution is to loop through all methods until you find the right one. 一个明显但不优雅的解决方案是循环使用所有方法,直到找到正确的方法。

Another option is to take advantage of the Linq Expression API: 另一个选择是利用Linq Expression API:

public static MethodInfo GetMethod(Expression<Action> expr)
{
    var methodCall = expr.Body as MethodCallExpression;
    if (methodCall == null)
        throw new ArgumentException("Expression body must be a method call expression");
    return methodCall.Method;
}


...

int dummy;
MethodInfo mi = GetMethod(() => C.M<int>(null, out dummy));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM