Generics in C#, using type of a variable as parameter

160,932

Solution 1

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.

Solution 2

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);

Solution 3

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.

Solution 4

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.

Share:
160,932
Stefano
Author by

Stefano

I'm a software developer. I like web comics, TV-shows, movies, PC games and books. Read my blog if you want to find out more about me.

Updated on July 08, 2022

Comments

  • Stefano
    Stefano almost 2 years

    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.

    • Mitch Wheat
      Mitch Wheat over 14 years
      you can't use a generic that way (first example)
    • Mitch Wheat
      Mitch Wheat over 14 years
      Also: why would a DoesEntityExist() method require a transaction parameter?
    • Jonas Elfström
      Jonas Elfström over 14 years
    • Guillermo Gutiérrez
      Guillermo Gutiérrez over 11 years
      @MitchWheat, maybe he want to check if the entity exists in a database.
    • G.Y
      G.Y over 8 years
      This is not a duplicate question. it is a different aspect of the other question - answers in the other question do not qualify as answers to this question.
  • Fredrik Mörk
    Fredrik Mörk over 14 years
    I think that the most important thing in this answer is ick. That and compile-time type safety.
  • Jon Skeet
    Jon Skeet over 14 years
    @Mitch: The trouble is that sometimes it's necessary. It's ugly and should be avoided wherever possible... but occasionally you need it.
  • Moslem Ben Dhaou
    Moslem Ben Dhaou about 11 years
    @JonSkeet: is there a way to adapt this using the dynamic keyword?
  • Jon Skeet
    Jon Skeet about 11 years
    @MoslemBenDhaou: No, because there's no instance to use for type inference here.
  • Moslem Ben Dhaou
    Moslem Ben Dhaou about 11 years
    @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<T>(uid, DataProvider.GetConnection()); }. I am missing how to get the concrete type of dataObject and passed it as T.
  • Jon Skeet
    Jon Skeet about 11 years
    @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.
  • Moslem Ben Dhaou
    Moslem Ben Dhaou about 11 years
    @JonSkeet: here is a new question : stackoverflow.com/questions/16491618/… (Thank you!)
  • Sonic Soul
    Sonic Soul over 10 years
    this is great! are there any downsides? seems like more elegant solution to the original question than using reflection??
  • Tom Bowers
    Tom Bowers over 10 years
    There's an error in the above code. Type doesn't have an Invoke() method. It should be called on the method variable instead.
  • zionpi
    zionpi about 10 years
    Any hint on refactor method to make existing method to accept generic type ,dear @JonSkeet
  • Jon Skeet
    Jon Skeet about 10 years
    @zionpi: I don't follow exactly what you mean, but it sounds like you should probably ask a new question with examples.
  • Grigory
    Grigory about 9 years
    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.
  • Bret
    Bret about 7 years
    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<typeof(input)>(input) when I can just do Sort(input)!