【问题标题】:How do you call a generic method with out parameters by reflection?如何通过反射调用没有参数的泛型方法?
【发布时间】:2013-07-30 21:40:23
【问题描述】:

假设我有一个像这样的类,包含一个带有 out 参数的泛型方法:

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

通过阅读其他几个问题(How to use reflection to call generic Method?Reflection on a static overloaded method using an out parameter)的答案,我认为我可以通过反射调用该方法,如下所示:

// 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);

但是mi 将返回 null。我尝试在types 数组中使用object 而不是int,但这也不起作用。

如何在调用MakeGenericMethod之前为泛型方法指定类型(out 参数所需)?

【问题讨论】:

  • 你的真实班级有M的重载吗?如果没有,您可以在不需要指定参数类型的情况下使用 GetMethod 变体。但这并不能回答您提出的问题。
  • 在这种特定情况下,我将能够通过不指定任何类型而只使用名称来解决它,正如@SLaks 所建议的那样。我仍然想知道指定模板类型数组的语法是什么,或者它是否不可能。
  • 我出错的地方是我认为必须传递类型数组才能使用outref 参数。事实并非如此...只要您通过某种方式获得了正确的MethodInfo,您就可以将参数数组传递给它,它会设置值。

标签: c# generics reflection


【解决方案1】:

这会让你调用方法:

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

并获取out参数:

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

【讨论】:

  • 是的 SLaks 我知道,你的权利一如既往:),我只是提出了这个,因为除了这个唯一的方法之外,OP 没有提到其他任何东西,我知道它是一个非常“独特”(lol)的解决方案。
【解决方案2】:

我仍然想知道指定模板类型数组的语法是什么,或者是否不可能

I don't think it's possible to pass that kind of detailed type specification to GetMethod[s]。我认为,如果您有许多这样的Ms 需要查看,您必须全部获取它们,然后通过MethodInfos 和包含的对象的各种属性进行过滤,例如在您的特殊情况:

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;

【讨论】:

    【解决方案3】:

    您传递的参数将找到M&lt;T&gt;(IEnumerable&lt;int&gt;, ref int)
    你需要找到M(IEnumerable&lt;T&gt;, ref T)refout的区别只存在于C#语言中;反射只有ref)。

    我不确定如何通过它;您可能需要遍历所有方法才能找到它。

    在一个不相关的注释上,你需要传递更多BindingFlags

    BindingFlags.Public | BindingFlags.Static
    

    【讨论】:

    • 从您的回答中不清楚,但 refout 之间的区别在这里应该无关紧要,只有泛型类型才重要。
    • @hvd:是的;我忘了提。
    【解决方案4】:

    这是一个众所周知的问题;要找到方法,你需要知道它的类型参数,但是不知道方法你不能知道它的类型参数...

    一个明显但不优雅的解决方案是循环遍历所有方法,直到找到正确的方法。

    另一种选择是利用 Linq 表达式 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));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多