In this post, we’ll explore how to dynamically create instances of generic classes using C# reflection. This technique can be handy when you need to work with generic types where the type arguments are not known at compile time. We’ll demonstrate how to achieve this by using the MakeGenericType
method and the Activator.CreateInstance
method.
Creating Instances of Generic Classes: When dealing with generic classes, the actual type arguments may not be known until runtime. To create an instance of a generic class dynamically, you can follow these steps:
-
Retrieve the Generic Type Definition: First, obtain the generic type definition. This is the type that includes the open generic type parameter(s), represented by placeholders like
T
orTKey
.Type genericTypeDefinition = typeof(List<>);
-
Specify Type Arguments: Define the type arguments you want to use. These type arguments will replace the open generic type parameters in the generic type definition.
Type[] typeArgs = { typeof(string) };
-
Create the Closed Generic Type: Use the
MakeGenericType
method to create a closed generic type by combining the generic type definition with the specified type arguments.Type closedGenericType = genericTypeDefinition.MakeGenericType(typeArgs);
-
Instantiate the Generic Type: Finally, use the
Activator.CreateInstance
method to create an instance of the closed generic type.object instance = Activator.CreateInstance(closedGenericType);
-
Type Casting: Depending on your requirements, you might want to cast the instance to the specific generic type to work with its members.
List<string> typedInstance = instance as List<string>;
Example Code Snippet: Here’s the complete code snippet demonstrating how to create an instance of a generic class using C# reflection:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Type genericTypeDefinition = typeof(List<>);
Type[] typeArgs = { typeof(string) };
Type closedGenericType = genericTypeDefinition.MakeGenericType(typeArgs);
object instance = Activator.CreateInstance(closedGenericType);
List<string> typedInstance = instance as List<string>;
Console.WriteLine("Instance created: " + (typedInstance != null));
}
}
Real-World Example: Repository Pattern with Different Data Types
The Scenario
Suppose you have the following scenario:
- You have defined a generic interface
IRepository<T>
with methods likeAdd
andGetById
. - There are concrete implementations of this interface:
UserRepository
andProductRepository
. - Each implementation handles a specific entity type (
User
andProduct
) and provides corresponding methods.
The Code
Let’s dive into the code that demonstrates how to dynamically create instances of these classes based on a given entity type:
using System;
using System.Linq;
using System.Reflection;
public interface IRepository<T>
{
void Add(T entity);
T GetById(int id);
}
public class UserRepository : IRepository<User>
{
public void Add(User entity) { /* Implementation */ }
public User GetById(int id) { return new User { Id = 1 }; }
}
public class ProductRepository : IRepository<Product>
{
public void Add(Product entity) { /* Implementation */ }
public Product GetById(int id) { return new Product { Id = 1 }; }
}
public interface IEntity
{
int Id { get; set; }
}
public class User : IEntity
{
public int Id { get; set; }
}
public class Product : IEntity
{
public int Id { get; set; }
}
class Program
{
static void Main()
{
Type repositoryType = typeof(IRepository<>);
Type entityType = typeof(Product);
Type closedRepositoryType = repositoryType.MakeGenericType(entityType);
// Find and create an instance of a class implementing the IRepository<T> interface
var repositoryImplementationType = Assembly.GetExecutingAssembly().GetTypes()
.FirstOrDefault(type =>
!type.IsAbstract && !type.IsInterface &&
type.GetInterfaces()
.Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == repositoryType && i.GetGenericArguments()[0] == entityType));
if (repositoryImplementationType != null)
{
var repositoryInstance = Activator.CreateInstance(repositoryImplementationType);
Console.WriteLine($"Repository instance created: {repositoryImplementationType.Name}");
}
else
{
Console.WriteLine("No suitable repository implementation found.");
}
}
}
Explanation
The code begins by defining the generic interface IRepository<T>
and providing concrete implementations for entities User
and Product
.
In the Main
method, we specify the generic repository type IRepository<>
and the desired entity type (Product
in this case). We then use reflection to find a class that implements the desired repository interface with the specified entity type argument. If a suitable implementation is found, we create an instance of that class and display its name.
Conclusion
Dynamic instantiation of generic classes using reflection can be extremely useful in scenarios where the type arguments are determined at runtime. By combining MakeGenericType
and Activator.CreateInstance
, you can easily work with generic types in a flexible and dynamic manner. Just remember that reflection has performance overhead, so use it judiciously.