Get generic argument type and value supplied to a generic method

前端 未结 1 814
一个人的身影
一个人的身影 2021-01-11 22:56

How do you get the argument value supplied to a closed/constructed generic method?

It\'s been a while since I haven\'t touched Reflection. All this used to be at the

相关标签:
1条回答
  • 2021-01-11 23:25

    The short answer is typeof(Q).

    The long answer (which tries to explain why you can't enumerate these types and you must write them specifically) goes like this:

    Each generic method (which is more generic than it's declaring class) has corresponding, distinct MethodInfo instances for all of its (ever) touched particularizations and another MethodInfo for the "template"/open method.

    You could use this to obtain what you want:

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

    Why is that ? It's because none of the "enumerating operations" in reflection return MethodInfo instances that refer to closed particularizations.

    If you enumerate the static methods declared by ConcreteFoo like so:

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

    you will get identical results. As far as GenericMethod and it's entourage of particularizations are concerned, you will get only the reflection object associated with GenericMethod (open variant).

    atTime2 will not contain an extra MethodInfo referring to the freshly jitted GenericMethod< bool >.

    But that not's really a bad thing, now is it ? GetMethods() should return consistent results and not have its results vary in time. The algebra of generic methods is actually quite nice when it comes to it's "navigation" operations:

    1. All open MethodInfos have IsGenericMethod = true and IsGenericMethodDefinition = true
    2. All closed MethodInfos have IsGenericMethod = true and IsGenericMethodDefinition = false
    3. By calling .GetGenericMethodDefinition() on a closed MethodInfo you get the open one
    4. By calling .MakeGenericType(params Type[] types) on an open MethodInfo you get whatever closed one you want (without being syntactically aware of what those types are and with the possibility of receiving an exception for not respecting the where clauses)

    The same goes for the reflection operations that come from the current thread's perspective (rather than from that of the assemblies and types):

    MethodBase MethodInfo.GetCurrentMethod()
    

    and

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

    never return the actual closed variants of generic methods (if any) that are actually on the top, or throughout the current call stack.

    In a way your question is not absurd, because, while in the case of GetCurrentMethod you could easily replace it with GetCurrentMethod plus MakeGenericMethod plus the syntactically available typeof(Whatever), you can't say that about your callers.

    So.. for non-generic methods you can always look at your stack and know precisely what are those methods' parameter types. The methods that invoked each other and eventually yours got invoked... But for generic ones (which are really truly closed, for I repeat it is illogical to think that a generic method that runs and calls another and was called by someone else (etc) is an open one) you can't find out the types of the parameters just like you can't learn the values of any such methods' local variables (which are deterministic but it would be a great flaw in performance to make that a possibility).

    0 讨论(0)
提交回复
热议问题