views:

298

answers:

3

I want to call my generic method with a given type object.

void Foo(Type t)
{
     MyGenericMethod<t>();
}

obviously doesn't work.

How can I make it work?

A: 

This approach will not work. The reason why is that Type is an object who's type is determined at runtime. However you are trying to use it to call a generic method. A generic method call's type is established at compile time. Hence a Type object can't ever be used for a type parameter on a generic method.

JaredPar
Then what's the solution ? I am also into this topic so much. Thanks.
Braveyard
+6  A: 

You need to use reflection, unfortunately (for the reasons Jared mentioned). For example:

MethodInfo method = typeof(Foo).GetMethod("MyGenericMethod");
method = method.MakeGenericMethod(t);
method.Invoke(this, new object[0]);

Obviously you'd want more error checking in reality :)


Side note: my local MSDN doesn't specify that the parameter from MakeGenericMethod is a parameter array, so I'd have expected to require:

method = method.MakeGenericMethod(new Type[] { t });

but it seems it is a parameter array in reality, and the online MSDN docs agree. Odd.

Jon Skeet
argghh! The Skeet got me beat :/
Erik van Brakel
Thanks for reply, Jon. But I have already given the right method, why do I need to load them by name? Isn't there are more safe way which doesn't involve method names as strings?
codymanix
Cody: I added another alternative in my answer (trying to beat the Skeet) which doesn't involve strings. It's not as clean as I want, but the idea is clear I think.
Erik van Brakel
I wonder why there isn't a refactoring-safe way to pass method names around in C# programs. Can we blame Mr. Hejlsberg for that? :)
codymanix
You're after the infamous infoof operator. It's on the list of desirable features, but it just hasn't got high enough yet...
Jon Skeet
This is not the first time where I would have needed such an operator. Where can I vote for it?
codymanix
+2  A: 

Your code sample won't work, because the generic method expects a type identifier, not a an instance of the Type class. You'll have to use reflection to do it:

public class Example {

    public void CallingTest()
    {
        MethodInfo method = typeof (Example).GetMethod("Test");
        MethodInfo genericMethod = method.MakeGenericMethod(typeof (string));
        genericMethod.Invoke(this, null);

    }

    public void Test<T>()
    {
        Console.WriteLine(typeof (T).Name);
    }
}

Do keep in mind that this is very brittle, I'd rather suggest finding another pattern to call your method.

Another hacky solution (maybe someone can make it a bit cleaner) would be to use some expression magic:

public class Example {

    public void CallingTest()
    {
        MethodInfo method = GetMethod<Example>(x => x.Test<object>());
        MethodInfo genericMethod = method.MakeGenericMethod(typeof (string));
        genericMethod.Invoke(this, null);

    }

    public static MethodInfo GetMethod<T>(Expression<Action<T>> expr)
    {
        return ((MethodCallExpression) expr.Body)
            .Method
            .GetGenericMethodDefinition();
    }

    public void Test<T>()
    {
        Console.WriteLine(typeof (T).Name);
    }
}

Note passing the 'object' type identifier as a generic type argument in the lambda. Couldn't figure out so quickly how to get around that. Either way, this is compile-time safe I think. It just feels wrong somehow :/

Erik van Brakel
what is "Program"?
codymanix
My bad, I changed around some names (tested in a default C# console app, so the class is called Program ;)) Fixed it for you, it should be the name of the class you want to call the method on.
Erik van Brakel
Great idea with the Expressions. I also can not think of a way to refer to a generic method without specifying at least a generic dummy argument..
codymanix