获取提供给泛型方法的泛型参数类型和值

时间:2013-02-05 12:13:18

标签: c# .net reflection

如何获得提供给封闭/构造的泛型方法的参数值?

我已经有一段时间没有触及反射。所有这一切都在我的后面,嗯,无论如何。

class Program
{
    static void Main(string[] args)
    {
        new ConcreteFoo().GenericMethod<int>(5);
        Console.ReadKey();
    }
}

class ConcreteFoo
{
    public void GenericMethod<Q>(Q q) 
    {
        var method = MethodInfo.GetCurrentMethod();    
        var parameters = method.GetParameters();    
        if (parameters.Length > 0)
            foreach (var p in parameters)
                Console.WriteLine("Type: {0}", p.ParameterType);

        // That still prints Q as the type. 
        // I've tried GetGenericArguments as well. No luck.                
        // I want to know:
        // 1) The closed type, i.e. the actual generic argument supplied by the caller; and
        // 2) The value of that argument
    }

    public void GenericMethodWithNoGenericParameters<Q>() 
    { 
        // Same here
    }
}

class GenericFoo<T>
{
    public void NonGenericMethod(T t) { /* And here*/ }  
    public void GenericMethod<Q>(Q q) { /* And here */ }
}

更新

这个问题很荒谬,因此被提问者关闭了。他希望保留它只是为了向他的孩子们展示愚蠢的爸爸是多么愚蠢,如果他们原来是C#程序员的话。

1 个答案:

答案 0 :(得分:8)

简短回答是typeof(Q)。

长答案(试图解释为什么你不能枚举这些类型,你必须专门编写它们)是这样的:

每个泛型方法(比它的声明类更通用)具有相应的,不同的MethodInfo实例,用于所有(曾经)触摸的特殊化,另一个MethodInfo用于“模板”/ open方法。

你可以用它来获得你想要的东西:

class ConcreteFoo {    
   public void GenericMethod<Q>(Q q) {
     var method = MethodInfo.GetCurrentMethod();
     var closedMethod = method.MakeGenericMethod(typeof(Q));

     // etc
   }
}

为什么? 这是因为反射中的“枚举操作”都没有返回引用闭合特殊化的MethodInfo实例。

如果枚举ConcreteFoo声明的静态方法,如下所示:

var atTime1 = typeof(ConcreteFoo).GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);

ConcreteFoo.GenericMethod( true );

var atTime2 = typeof(ConcreteFoo).GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);

你会得到相同的结果。 就GenericMethod及其随行人员而言,您将只获得与GenericMethod(开放变体)相关联的反射对象。

atTime2将不包含一个额外的MethodInfo,引用新近插入的GenericMethod&lt; bool&gt;。

但这不是一件坏事,现在是吗? GetMethods()应返回一致的结果,并且其结果不会随时间变化。 当涉及到它的“导航”操作时,泛型方法的代数实际上非常好:

  1. 所有打开的MethodInfos都有IsGenericMethod = true且IsGenericMethodDefinition = true
  2. 所有已关闭的MethodInfos具有IsGenericMethod = true且IsGenericMethodDefinition = false
  3. 通过在已关闭的MethodInfo上调用.GetGenericMethodDefinition(),您将获得打开的
  4. 通过在打开的MethodInfo上调用.MakeGenericType(params Type []类型),你得到任何你想要的关闭(没有语法上知道那些类型是什么,并且有可能接收不遵守where子句的异常)
  5. 对于来自当前线程视角的反射操作(而不是来自组件和类型的反射操作)也是如此:

    MethodBase MethodInfo.GetCurrentMethod()
    

    StackTrace trace = new StackTrace();
    IEnumerable<MethodBase> methods = from frame in trace.GetFrames()
                                      select frame.GetMethod();
    

    永远不会返回泛型方法的实际封闭变体(如果有的话) 它实际上位于顶部或整个当前的调用堆栈中。

    在某种程度上,你的问题并不荒谬,因为,在 GetCurrentMethod 的情况下 您可以轻松地将其替换为 GetCurrentMethod 加上 MakeGenericMethod 以及语法上可用的 typeof(Whatever),您无法说出您的来电者。< / p>

    因此..对于非泛型方法,您可以随时查看堆栈并准确了解这些方法的参数类型。相互调用并最终调用你的方法被调用了...但是对于通用的方法(它们真的是真正关闭的,因为我重复一遍,认为运行并调用另一个并由其他人调用的通用方法是不合逻辑的(等等) )是一个开放的)你无法找到参数的类型,就像你无法学习任何这样的方法的局部变量的值(这是确定性的,但这将是一个很大的性能缺陷,使这个可能性)。