10

クローズド/構築されたジェネリック メソッドに渡された引数値を取得するにはどうすればよいですか?

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# プログラマーであることが判明した場合に、父親がどれほど愚かであったかを子供たちに示すためだけに、それを保持したいと考えています。

4

1 に答える 1

8

簡単な答えは typeof(Q) です。

長い答え (これらの型を列挙できず、具体的に記述しなければならない理由を説明しようとするもの) は次のようになります。

各ジェネリック メソッド (宣言クラスよりもジェネリックである) には、(これまでに) 触れたすべての詳細化に対応する個別の MethodInfo インスタンスと、「テンプレート」/open メソッド用の別の 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 には、新しく jit された GenericMethod< bool > を参照する追加の MethodInfo は含まれません。

しかし、それは本当に悪いことではありません。GetMethods() は一貫した結果を返す必要があり、結果が時間とともに変化しないようにする必要があります。ジェネリック メソッドの代数は、「ナビゲーション」操作に関しては、実際には非常に優れています。

  1. 開いているすべての MethodInfo には、IsGenericMethod = true および IsGenericMethodDefinition = true があります。
  2. クローズされたすべての MethodInfo は、IsGenericMethod = true および IsGenericMethodDefinition = false です。
  3. 閉じた MethodInfo で .GetGenericMethodDefinition() を呼び出すと、開いている MethodInfo を取得できます
  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 の場合、 GetCurrentMethodMakeGenericMethod加えて構文的に利用可能なtypeof(Whatever) に簡単に置き換えることができるため、質問はばかげているわけではありませんが、呼び出し元についてそれを言うことはできません。

したがって、非ジェネリック メソッドの場合は、いつでもスタックを見て、それらのメソッドのパラメーターの型が何であるかを正確に知ることができます。互いに呼び出し、最終的にあなたのメソッドが呼び出された...しかし、ジェネリックメソッドの場合(これは本当に本当に閉じています。繰り返しますが、ジェネリックメソッドが実行されて別のメソッドを呼び出し、他の誰かによって呼び出されたと考えるのは非論理的です(など) )はオープンなものです)そのようなメソッドのローカル変数の値を学習できないのと同じように、パラメーターのタイプを見つけることはできません(これは決定論的ですが、それを可能性)。

于 2013-02-23T23:27:09.287 に答える