views:

167

answers:

4

Consider the following code:

class Program {
    void Foo<T>() { }

    static void Main(string[] args) {
        dynamic p = new Program();
        p.Foo();
    }
}

Note surprisingly, the call to p.Foo() is not valid because the dynamic binder has no way of knowing what type to use for T. The specific failure is:

"The type arguments for method 'ConsoleApplication1.Program.Foo()' cannot be inferred from the usage. Try specifying the type arguments explicitly."

Now my question is: is there a way to specify the generic type, or is such method simply not callable using 'dynamic'?

+2  A: 

Why not specify the type exactly as you would for a non-dynamic type

p.Foo<int>();
JaredPar
Sorry, my question wasn't clear. See clarification I added to the question.
David Ebbo
+1  A: 

In this case, it doesn't matter what type "p" is, whether you declare it as dynamic or as Program, you will receive this error.

The error is saying that the type of T cannot be inferred because there are no parameters of type T being passed to the method, and the method isn't part of a generic class. In this case, there's no way for the compiler to infer what type T is.

You should be able to do the following, though:

class Program {
    void Foo<T>() { }

    static void Main(string[] args) {
        dynamic p = new Program();
        p.Foo<int>();
    }
}

You just need to explicitly define the type of T when you call Foo.

Reed Copsey
Yes, it's clear why the code doesn't work and what the error means. The question is how to pass T in scenarios where T is only known at runtime (see clarification comment in the original question).
David Ebbo
A: 

Why couldn't you specify that T is dynamic?

p.Foo<dynamic>();
dahlbyk
Because the compiler will assume that you mean "p.Foo<object>". "dynamic" is just "object" with the additional semantics of "generate dynamic call sites on things of this type".
Eric Lippert
dynamic is not *really* a type in the sense that it characterizes the object at compile time. Rather, it is an Object, with *dynamic* symantecs at runtime. Given this, I doubt that the target invocation could make use of it, since the dynamic type is unique to c# (it is not be present in IronPython or IronRuby, AFAIK).
Robert Harvey
I guess that makes sense since within Foo it's statically typed as T.
dahlbyk
+10  A: 

As Jared says, you can specify it in code just as you would for a static call:

using System;

class Program {
    void Foo<T>() {
        Console.WriteLine(typeof(T));
    }

    static void Main(string[] args) {
        dynamic p = new Program();
        p.Foo<string>();
    }
}

The above code prints System.String.

Now if you only know T at execution time, it's slightly harder. If you have an instance of it though, you could use dynamic typing and type inference together:

using System;

class Program {
    void Foo<T>() {
        Console.WriteLine(typeof(T));
    }

    static void Main(string[] args) {
        dynamic p = new Program();
        dynamic v = GetRandomInstance();

        // Now to call p.Foo<T> where T is the type of v's value...
        Dummy(v, p);
    }

    static void Dummy<T>(T t, Program p) {
        p.Foo<T>();
    }

    static object GetRandomInstance() {
        return DateTime.Now.Hour > 10 ? "hello" : (object) 10;
    }
}

EDIT: Pavel came up with an amazing idea in the comments. You don't need to come up with an instance of T, just an array. This means you can even use type arguments where you wouldn't normally be able to get an instance of T (e.g. due to a private constructor):

using System;

class PrivateConstructor {
    private PrivateConstructor() {}
}

class Program {
    static void Foo<T>() {
        Console.WriteLine(typeof(T));
    }

    static void CallFooProxy<T>(T[] array) {
        Foo<T>();
    }

    static void CallFoo(Type t) {
        dynamic array = Array.CreateInstance(t, 0);
        CallFooProxy(array);
    }

    static void Main(string[] args) {
        CallFoo(typeof(PrivateConstructor));
    }
}

Before anyone asks - no, this doesn't let you call Foo<Enumerable> dynamically - you still can't use a static class as a type argument, even if you try to delay the attempt until execution time :)

If all of that fails for some reason, it's back to reflection as normal... get the method info, call MakeGenericMethod and invoke it.

Jon Skeet
Dude. Slick. -
Eric Lippert
Well you guys *implemented* the thing. Somehow just *using* it pales in comparison :)
Jon Skeet
That's pretty nifty, Jon!Unfortunately, in my scenario I don’t have an instance of the type, I just have the System.Type, which apparently makes me out of luck! I guess I can create a dummy instance via Activator.CreateInstance just to make the binder happy, but that assumes ctor knowledge. I might just be better off staying with the traditional Reflection/MakeGenericMethod approach.Still marking this as 'answer', as it's probably as good as it can get with the current C# feature.
David Ebbo
Note that Reflection won't cover all scenarios that `dynamic` does (e.g. consider what happens if the receiver is a Python object).
Pavel Minaev
If you make `Dummy<T>` use an argument of type `T[]` rather than plain `T` for deduction purposes (which should still work), you can then use `Array.CreateInstance(runtimeType, 0)` to create a dummy zero-length array regardless of presence of accessible default constructors on type `T`.
Pavel Minaev
@Pavel: That's an awesome idea. Mind if I add it to the answer?
Jon Skeet
@Jon: Of course I don't mind! Besides, the awesome idea was yours, I merely rode the wave :)
Pavel Minaev
@Pavel @Jon: thanks for the new twist!
David Ebbo