Initializing a Generic variable from a C# Type Variable
C#GenericsC# Problem Overview
I have a class that takes a Generic Type as part of its initialization.
public class AnimalContext<T>
{
public DoAnimalStuff()
{
//AnimalType Specific Code
}
}
What I can do right now is
AnimalContext<Donkey> donkeyContext = new AnimalContext<Donkey>();
AnimalContext<Orca> orcaContext = new AnimalContext<Orca>();
But what I need/want to do is be able to declare an AnimalContext initialized to a type that is only known at runtime. For instance,
Animal a = MyFavoriteAnimal(); //returns an instance of a class
//implementing an animal
AnimalContext<a.GetType()> a_Context = new AnimalContext<a.GetType()>();
a_Context.DoAnimalStuff();
Is this even possible? I can't seem to find an answer for this online.
C# Solutions
Solution 1 - C#
What you mean by this part is possible:
new AnimalContext<a.GetType()>();
Obviously that exact syntax is wrong, and we'll get to that, but it is possible to construct an instance of a generic type at runtime when you don't know the type parameters until runtime.
What you mean by this part is not:
AnimalContext<a.GetType()> a_Context
That is, it is impossible to type a variable as a generic type if you don't know the type parameters at compile-time. Generics are compile-time constructs, and rely on having the type information available at compile-time. Given this, you lose all the benefits of generics if you don't know the types at compile-time.
Now, to construct an instance of a generic type at runtime when you don't know the type until runtime, you can say:
var type = typeof(AnimalContext<>).MakeGenericType(a.GetType());
var a_Context = Activator.CreateInstance(type);
Note that the compile-time type of a_context
is object
. You will have to cast a_context
to a type or interface that defines the methods you need to access. Often what you'll see people do here is have the generic type AnimalContext<T>
implement some interface (say IAnimalContext
) or inherit from a non-generic base class (say AnimalContext
) that defines the methods they need (so then you can cast a_context
to the interface or the non-generic base class). Another alternative is to use dynamic
. But again, keep in mind, you have none of the benefits of generic types in doing this.
Solution 2 - C#
You can use reflection with generic type by using MakeGenericType
method and take adavantage of dynamic
keyword:
var type = typeof (AnimalContext<>).MakeGenericType(a.GetType());
dynamic a_Context = Activator.CreateInstance(type);
So you can call:
a_Context.DoAnimalStuff();
Or use reflection again to call method:
type.GetMethod("DoAnimalStuff").Invoke(a_Context, null);
Solution 3 - C#
You would need to create the type using Reflection and then invoke that type. Something like:
Animal a = MyFavoriteAnimal();
var contextType = typeof(EsbRepository<>).MakeGenericType(a.GetType());
dynamic context = Activator.CreateInstance(contextType);
context.DoAnimalStuff();
The use of dynamic means that the context variable will be evaluated at run time allowing for you to call the DoAnimalStuff method.