11

How can I use a dynamic as a generic?

This

var x = something not strongly typed;
callFunction<x>();

and this

dynamic x = something not strongly typed;
callFunction<x>();

both produce this error

Error   1   The type or namespace name 'x' 
could not be found (are you missing a using directive or an assembly reference?)

What can I do to x to make it legitimate enough to be used in <x>?

svick
  • 236,525
  • 50
  • 385
  • 514
Travis J
  • 81,153
  • 41
  • 202
  • 273
  • 2
    Your code is confusing. It looks like you're trying to pass a variable as a type parameter? This won't work anyway. It needs to be a type. – M.Babcock Apr 12 '12 at 22:26
  • @M.Babcock - even when converted to a type the same error is issued. For example: `Type t; t = LegitimateObject.GetType(); callFunction();` produces the same error. – Travis J Apr 12 '12 at 22:28
  • 4
    No not a type as in `System.Type` a type as in `string` or `int` directly. Variables cannot be used as type parameters. – M.Babcock Apr 12 '12 at 22:29
  • `var x` does not mean that the variable `x` is not strongly typed. The compiler infers (guesses if you prefer) the type of the expression to the right of `=` and makes it the type of `x`. If The compiler thinks that the expression is of type `string` then `var x` means exactly the same as `string x`. Note also that this happens at compile time, not at runtime! – Olivier Jacot-Descombes Apr 12 '12 at 22:32
  • You example code doesn't make sense: Consider your code if x *were* strongly typed: `string x = "X";`. You don't call `CallFunction()` or `CallFunction()`; you call `CallFunction()` This doesn't make sense unless CallFunction has a string parameter or return value; A more likely call would be `string x = CallFunction();` or `CallFunction(x)`. Can you give an example of the function you'd like to call? What are you trying to achieve? – phoog Apr 12 '12 at 22:49
  • @phoog - There is no try ;) I did manage to achieve this with the immaculate help of JonSkeet. See his answer and the comments below for some better context. – Travis J Apr 12 '12 at 23:05
  • [populating a list control](https://stackoverflow.com/a/15466468/2932052) seemed a good use case for this – Wolf May 14 '18 at 10:28

6 Answers6

21

You could use type inference to sort of trampoline the call:

dynamic x = something not strongly typed;
CallFunctionWithInference(x);

...

static void CallFunctionWithInference<T>(T ignored)
{
    CallFunction<T>();
}

static void CallFunction<T>()
{
    // This is the method we really wanted to call
}

This will determine the type argument at execution time based on the execution-time type of the value of x, using the same kind of type inference it would use if x had that as its compile-time type. The parameter is only present to make type inference work.

Note that unlike Darin, I believe this is a useful technique - in exactly the same situations where pre-dynamic you'd end up calling the generic method with reflection. You can make this one part of the code dynamic, but keep the rest of the code (from the generic type on downwards) type-safe. It allows one step to be dynamic - just the single bit where you don't know the type.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Hah! This works, well with a little adaptation. Pure genius. I love it. The problem is that with your current definition T must be a reference type to use it as a parameter later in the `CallFunction`. To improve on this, I made these edits: `static void CallFunctionWithInference(T ignored) where T : class` and `static void CallFunctionWithInference(T ignored) where T : class`. Thanks again! I wish I had more than +1. – Travis J Apr 12 '12 at 22:45
  • 3
    @TravisJ: Is that because `CallFunction` itself has a constraint? You didn't tell us that in the question :) Without the constraint on `CallFunction` it should be fine though. – Jon Skeet Apr 12 '12 at 22:47
  • Yes, sorry for the omission :) The constraint in `CallFunction` is an initialization (down the line) of an EF DbSet in the form of `this.dbSet = context.Set();` which is inside a generic repository called from a using statement. At the time I did not realize it would affect the outcome in this way or I would have included it. – Travis J Apr 12 '12 at 22:49
  • The first comment should read "The problem **on my end** is that..." – Travis J Apr 12 '12 at 22:51
  • @TravisJ: Righto - that makes sense :) – Jon Skeet Apr 12 '12 at 22:57
  • I bountied this answer because of its awesomeness. – Travis J May 02 '12 at 21:18
  • I experienced some odd behavior using this when trying to make `x` with `Activator.CreateInstance`. I have struggled for a few days with it and was hoping you could provide some insight? I am getting an exception: outer: `Object reference not set to an instance of an object.` inner: `Microsoft.CSharp.RuntimeBinder.SymbolTable.GetOriginalTypeParameterType(Type t) +10`. Here is the code: `dynamic AnyObject = Activator.CreateInstance("MyAssembly", "MyAssembly.Models.DbModels." + entityType).Unwrap();CallWithInference(AnyObject);` What did I do wrong? – Travis J Aug 23 '12 at 16:32
  • 1
    @TravisJ: I think it would help if you could create a new question for this, with a short but *complete* program demonstrating the problem. – Jon Skeet Aug 23 '12 at 16:33
  • I made a complete example, but was unable to exactly reproduce the problem. The question is here: http://stackoverflow.com/q/12096695/1026459 – Travis J Aug 23 '12 at 17:03
5

It's hard to tell what exactly are you trying to do. But if you want to call a generic method with a type parameter that is the same as some object, you can't do that directly. But you can write another method that takes your object as a parameter, let the dynamic infer the type and then call the method you want:

void HelperMethod<T>(T obj)
{
    CallFunction<T>();
}

…

dynamic x = …;
HelperMethod(x);
svick
  • 236,525
  • 50
  • 385
  • 514
  • Wow.. this is awesome.. A subtle difference is that when `x` is null, type inference mechanism fails here. If `x` is strongly typed, the method call itself wont fail. Just something to have in mind.. – nawfal Jan 16 '14 at 15:07
2

You can't. The whole point of generics is compile-time safety which means that they must be known at compile-time. And the whole point of dynamics is that you don't need to know the exact type at compile time and use runtime dispatching => it's the absolutely exact opposite of generics. So don't waste your time => once you get the dynamic/reflection path you can forget about generics and compile-time safety. You will have to walk that path till the end.

So to answer your question:

What can I do to x to make it legitimate enough to be used in ?

The only thing you could do is to use a type that is known at compile-time, otherwise you cannot use generics.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Well, you kind of can use generics with a type that is not known at compile time, see my answer. – svick Apr 12 '12 at 22:36
1

You get that error because x is not a type. You need to specify a type as a type parameter.

In fact, you can use dynamic as a type parameter if you use it correctly:

var dict = new Dictionary<string, dynamic>();

dict.Add("Item1", 123);
dict.Add("Item2", "Blah");

This compiles and runs just fine.

M.Babcock
  • 18,753
  • 6
  • 54
  • 84
0

The quickest way to make this work is to make your anonymous type a real type.

So instead of

var x = new { Value = "somevalue", Text = "sometext" };

You need to do

class MyClass
{
    string Text, Value;
}
....
var x = new MyClass() { Value = "somevalue", Text = "sometext" };
//this should work now
callFunction<MyClass>(x);
Biff MaGriff
  • 8,102
  • 9
  • 61
  • 98
0

You should be able to call the function like this

callFunction<dynamic>();

If your function is defined as

public void callFunction<T>(T arg) {
    ...
}

You can simply call it with

callFunction(x);

C# is able to infer generic type parameters in many situations.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188