144

I have a generic method

bool DoesEntityExist<T>(Guid guid, ITransaction transaction) where T : IGloballyIdentifiable;

How do I use the method in the following way:

Type t = entity.GetType();
DoesEntityExist<t>(entityGuid, transaction);

I keep receiving the foollowing compile error:

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

DoesEntityExist<MyType>(entityGuid, transaction);

works perfectly but I do not want to use an if directive to call the method with a separate type name every time.

Germstorm
  • 9,709
  • 14
  • 67
  • 83

4 Answers4

190

The point about generics is to give compile-time type safety - which means that types need to be known at compile-time.

You can call generic methods with types only known at execution time, but you have to use reflection:

// For non-public methods, you'll need to specify binding flags too
MethodInfo method = GetType().GetMethod("DoesEntityExist")
                             .MakeGenericMethod(new Type[] { t });
method.Invoke(this, new object[] { entityGuid, transaction });

Ick.

Can you make your calling method generic instead, and pass in your type parameter as the type argument, pushing the decision one level higher up the stack?

If you could give us more information about what you're doing, that would help. Sometimes you may need to use reflection as above, but if you pick the right point to do it, you can make sure you only need to do it once, and let everything below that point use the type parameter in a normal way.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 20
    I think that the most important thing in this answer is *ick*. That and *compile-time type safety*. – Fredrik Mörk Jan 21 '10 at 08:42
  • 11
    @Mitch: The trouble is that *sometimes* it's necessary. It's ugly and should be avoided wherever possible... but occasionally you need it. – Jon Skeet Jan 21 '10 at 08:44
  • @JonSkeet: is there a way to adapt this using the `dynamic` keyword? – Moslem Ben Dhaou May 10 '13 at 17:56
  • 1
    @MoslemBenDhaou: No, because there's no instance to use for type inference here. – Jon Skeet May 10 '13 at 19:21
  • @JonSkeet: well here is my situation, a bit similar: I have an instance of an object passed as an interface variable to a method calling a generic API method: `public static Boolean PurgeDataObject(this IDataObject dataObject, Guid uid) { return DataProvider.DeleteDataObject(uid, DataProvider.GetConnection()); }`. I am missing how to get the concrete type of dataObject and passed it as T. – Moslem Ben Dhaou May 10 '13 at 21:04
  • 2
    @MoslemBenDhaou: Well you could go via an intermediate generic method using `dynamic` to infer the type. If that's not enough information, ask a new question - it's sufficiently different from this one that pursuing it in comments isn't ideal. – Jon Skeet May 10 '13 at 21:11
  • 1
    @JonSkeet: here is a new question : http://stackoverflow.com/questions/16491618/pass-concrete-object-type-as-parameter-for-generic-method (Thank you!) – Moslem Ben Dhaou May 10 '13 at 22:23
  • There's an error in the above code. `Type` doesn't have an `Invoke()` method. It should be called on the `method` variable instead. – Tom Bowers Nov 11 '13 at 08:49
40

One way to get around this is to use implicit casting:

bool DoesEntityExist<T>(T entity, Guid guid, ITransaction transaction) where T : IGloballyIdentifiable;

calling it like so:

DoesEntityExist(entity, entityGuid, transaction);

Going a step further, you can turn it into an extension method (it will need to be declared in a static class):

static bool DoesEntityExist<T>(this T entity, Guid guid, ITransaction transaction) where T : IGloballyIdentifiable;

calling as so:

entity.DoesEntityExist(entityGuid, transaction);
Joe Lloyd
  • 544
  • 3
  • 4
  • this is great! are there any downsides? seems like more elegant solution to the original question than using reflection?? – Sonic Soul Oct 09 '13 at 18:51
  • 1
    It only works if declared type is enough. What if entity is declared as object ? The solution of JonSkeet creates generic version where T is real type, not the declared one. – Grigory May 05 '15 at 10:07
  • I didn't know that using the T constraint allowed you to bypass the type parameter when you call the method. I was trying to figure out something like `Sort(input)` when I can just do `Sort(input)`! – Bret Feb 22 '17 at 15:28
11

I'm not sure whether I understand your question correctly, but you can write your code in this way:

bool DoesEntityExist<T>(T instance, ....)

You can call the method in following fashion:

DoesEntityExist(myTypeInstance, ...)

This way you don't need to explicitly write the type, the framework will overtake the type automatically from the instance.

kosto
  • 151
  • 3
5

You can't use it in the way you describe. The point about generic types, is that although you may not know them at "coding time", the compiler needs to be able to resolve them at compile time. Why? Because under the hood, the compiler will go away and create a new type (sometimes called a closed generic type) for each different usage of the "open" generic type.

In other words, after compilation,

DoesEntityExist<int>

is a different type to

DoesEntityExist<string>

This is how the compiler is able to enfore compile-time type safety.

For the scenario you describe, you should pass the type as an argument that can be examined at run time.

The other option, as mentioned in other answers, is that of using reflection to create the closed type from the open type, although this is probably recommended in anything other than extreme niche scenarios I'd say.

Rob Levine
  • 40,328
  • 13
  • 85
  • 111