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

本文关键字:参数 类型 泛型 泛型方法 获取 | 更新日期: 2023-09-27 17:57:49

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

我已经有一段时间没有碰反射了。所有这些过去都在我的背后,嗯,不管怎样。

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

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

简短的答案是(Q)类型。

答案很长(试图解释为什么你不能列举这些类型,你必须专门写它们)如下:

每个泛型方法(比它的声明类更通用)都有对应的、不同的MethodInfo实例,用于所有(曾经)接触过的特殊化,以及另一个MethodInfo实例用于"template"/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将不包含引用新编译的GenericMethod<bool>。

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

  1. 所有打开的MethodInfos都具有IsGenericMethod=true和IsGenericMethodDefinition=true
  2. 所有已关闭的MethodInfos的IsGenericMethod=true,IsGenericMethodDefinition=false
  3. 通过对关闭的MethodInfo调用.GetGenericMethodDefinition(),可以获得打开的MethodInfo
  4. 通过在打开的MethodInfo上调用.MakeGenericType(paramsType[]类型),您可以获得所需的任何关闭类型(在语法上不知道这些类型是什么,并且可能会因为不尊重where子句而收到异常)

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

MethodBase MethodInfo.GetCurrentMethod()

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

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

在某种程度上,你的问题并不荒谬,因为在GetCurrentMethod的情况下你可以很容易地用GetCurrentMethod加上MakeGenericMethod型(Whatever)来替换它,但你不能这么说你的调用者。

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