10

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

我已经有一段时间没有接触反射了。这一切过去都在我的背后,嗯,无论如何。

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# 程序员的话。

4

1 回答 1

8

简短的回答是 typeof(Q)。

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

每个泛型方法(比它声明的类更通用)对于它的所有(曾经)接触的具体化具有相应的、不同的 MethodInfo 实例,并且对于“模板”/打开方法具有另一个 MethodInfo。

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

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 引用新 jitted 的 GenericMethod<bool>。

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

  1. 所有打开的 MethodInfos 都有 IsGenericMethod = true 和 IsGenericMethodDefinition = true
  2. 所有关闭的 MethodInfos 都有 IsGenericMethod = true 和 IsGenericMethodDefinition = false
  3. 通过在一个封闭的 MethodInfo 上调用 .GetGenericMethodDefinition() 你得到一个开放的
  4. 通过在打开的 MethodInfo 上调用 .MakeGenericType(params Type[] types) ,您可以获得所需的任何已关闭的类型(在语法上不知道这些类型是什么,并且有可能因不遵守 where 子句而收到异常)

从当前线程的角度来看(而不是从程序集和类型的角度)的反射操作也是如此:

MethodBase MethodInfo.GetCurrentMethod()

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

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

在某种程度上,您的问题并不荒谬,因为在GetCurrentMethod的情况下, 您可以轻松地将其替换为GetCurrentMethod加上MakeGenericMethod加上语法上可用的typeof(Whatever),但您不能对调用者这么说。

所以.. 对于非泛型方法,您始终可以查看您的堆栈并准确知道这些方法的参数类型是什么。相互调用并最终调用你的方法......但是对于通用方法(它们确实是真正封闭的,因为我再说一遍,认为运行和调用另一个并被其他人调用的通用方法是不合逻辑的(等等)是一个开放的)你无法找出参数的类型,就像你无法学习任何此类方法的局部变量的值一样(这是确定性的,但如果让它成为一个巨大的性能缺陷)可能性)。

于 2013-02-23T23:27:09.287 回答