Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get generic argument type and value supplied to a generic method

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 back of my, umm, whatever.

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 */ }
}

UPDATE

This question is absurd and hence closed by the asker. He wishes to retain it just to show his children how stupid daddy was, if they ever turned out to be C# programmers.

like image 684
Water Cooler v2 Avatar asked Feb 05 '13 12:02

Water Cooler v2


People also ask

How do I get a class instance of generic type T?

The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. A solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.

What is generic type arguments?

The generic argument list is a comma-separated list of type arguments. A type argument is the name of an actual concrete type that replaces a corresponding type parameter in the generic parameter clause of a generic type. The result is a specialized version of that generic type.

What is generic type class?

A generic type is a generic class or interface that is parameterized over types.


1 Answers

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

like image 99
Eduard Dumitru Avatar answered Sep 19 '22 22:09

Eduard Dumitru