Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

Suppose I have a class like this, containing a generic method with an out parameter:

public class C
    public static void M<T>(IEnumerable<T> sequence, out T result)
        Console.WriteLine("Test");
        result = default(T);

From reading the answers to a couple of other questions (How to use reflection to call generic Method? and Reflection on a static overloaded method using an out parameter), I thought I might be able to invoke the method via reflection as follows:

// get the method
var types = new[] { typeof(IEnumerable<int>), typeof(int).MakeByRefType() };
MethodInfo mi = typeof(C).GetMethod(
    "M", BindingFlags.Static, Type.DefaultBinder, types, null);
// convert it to a generic method
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) });
// call it
var parameters = new object[] { new[] { 1 }, null };
generic.Invoke(null, parameters);

But mi is coming back null. I've tried using object instead of int in the types array but that doesn't work either.

How can I specify the types (needed for the out parameter) for a generic method before the call to MakeGenericMethod?

Does your real class have overloads of M? If not, you could just use the GetMethod variant where you don't need to specify the parameter types. That doesn't answer the question you asked though. – user743382 Jul 30, 2013 at 21:55 In this specific case I'll be able to work around it by not specifying any types and just using the name, as @SLaks suggested. I'm still interested to know what the syntax is for specifying an array of template types, or if it's not possible. – Matthew Strawbridge Jul 31, 2013 at 6:02 Where I was going wrong was that I thought it was necessary to pass the type array in order to use out or ref parameters. That's not the case... as long as you've got the correct MethodInfo by some means, you can pass it the parameter array and it will set the values. – Matthew Strawbridge Aug 1, 2013 at 8:41
MethodInfo mi = typeof(C).GetMethod("M");
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) });
var parameters = new object[] { new[]{1},null};
generic.Invoke(null, parameters);

And to get the out parameter:

Console.WriteLine((int)parameters[1]); //will get you 0(default(int)).
                Yes SLaks i know and your right as always :),i just presented this since OP didnt mention anything else besides this one only method,i know its a very "distinct"(lol) solution.
– terrybozzio
                Jul 31, 2013 at 1:59
  

I'm still interested to know what the syntax is for specifying an array of template types, or if it's not possible

I don't think it's possible to pass that kind of detailed type specification to GetMethod[s]. I think if you have a number of such Ms to look through, you have to get them all and then filter by the various properties of the MethodInfos and contained objects, eg as much of this as is necessary in your particular case:

var myMethodM =
    // Get all the M methods
    from mi in typeof(C).GetMethods()
    where mi.Name == "M"
    // that are generic with one type parameter
    where mi.IsGenericMethod
    where mi.GetGenericArguments().Length == 1
    let methodTypeParameter = mi.GetGenericArguments()[0]
    // that have two formal parameters
    let ps = mi.GetParameters()
    where ps.Length == 2
    // the first of which is IEnumerable<the method type parameter>
    where ps[0].ParameterType.IsGenericType
    where ps[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
    where ps[0].ParameterType.GetGenericArguments()[0] == methodTypeParameter
    // the second of which is ref <the method type parameter>
    where ps[1].ParameterType.IsByRef
    where ps[1].ParameterType.GetElementType() == methodTypeParameter
    select mi;

You've passed parameters that will find M<T>(IEnumerable<int>, ref int).
You need to find M(IEnumerable<T>, ref T) (the distinction between ref and out exists only in the C# language; reflection only has ref).

I'm not sure how to pass that; you may need to loop through all methods to find it.

On an unrelated note, you need to pass more BindingFlags:

BindingFlags.Public | BindingFlags.Static
                It isn't clear from your answer, but the difference between ref and out should not matter here, it's only the generic type that should matter.
– user743382
                Jul 30, 2013 at 21:49

This is a well known-problem; to find the method, you need to know its type parameter, but you can't know its type parameter without knowing the method first...

An obvious but inelegant solution is to loop through all methods until you find the right one.

Another option is to take advantage of the Linq Expression API:

public static MethodInfo GetMethod(Expression<Action> expr)
    var methodCall = expr.Body as MethodCallExpression;
    if (methodCall == null)
        throw new ArgumentException("Expression body must be a method call expression");
    return methodCall.Method;
int dummy;
MethodInfo mi = GetMethod(() => C.M<int>(null, out dummy));
        

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.