In C#, we typically use the new ClassName(...) syntax to create instances. However, in scenarios such as plugin systems or DI (Dependency Injection) container implementations, the type to be instantiated might not be determined until runtime.
In such cases, by combining the Reflection features Type.GetConstructor and ConstructorInfo.Invoke, you can specify the argument type signature, call a specific constructor, and create an object dynamically.
Dynamic Invocation of Constructors with Specific Arguments
The following sample code demonstrates how to identify and instantiate a constructor for a Product class that takes two arguments: an int (ID) and a string (Name).
Sample Code
using System;
using System.Reflection;
public class Program
{
public static void Main()
{
// 1. Get type information of the class to instantiate
// In reality, you might get this from a string like Type.GetType("Namespace.Product")
Type targetType = typeof(Product);
Console.WriteLine($"Target Type: {targetType.Name}");
// 2. Define the array of argument types for the constructor we want to call
// Here we target public Product(int id, string name)
Type[] constructorArgsTypes = new Type[]
{
typeof(int),
typeof(string)
};
// 3. Get constructor info matching the argument types
ConstructorInfo ctorInfo = targetType.GetConstructor(constructorArgsTypes);
if (ctorInfo != null)
{
// 4. Prepare actual arguments as an object array and execute the constructor (Invoke)
object[] executionArgs = new object[] { 1001, "Gaming Keyboard" };
// The return value of Invoke is object type, so cast if necessary
var createdInstance = ctorInfo.Invoke(executionArgs);
// Check result
Console.WriteLine("Instance creation successful.");
Console.WriteLine($"Created Object: {createdInstance}");
}
else
{
Console.WriteLine("Constructor with the specified signature was not found.");
}
}
}
// Sample Product Class
public class Product
{
public int Id { get; }
public string Name { get; }
// Target Constructor
public Product(int id, string name)
{
Id = id;
Name = name;
}
// Parameterless Constructor (Not used this time)
public Product()
{
Id = 0;
Name = "Unknown";
}
public override string ToString()
{
return $"[Product] ID:{Id}, Name:{Name}";
}
}
Explanation and Technical Points
1. Type.GetConstructor Method
Classes can have multiple constructors (overloads). Therefore, you must pass a Type array to the GetConstructor method to indicate “which combination of arguments (signature) you want to retrieve.”
- For parameterless constructors: Pass
Type.EmptyTypes. - If no match is found: It returns
null.
2. ConstructorInfo.Invoke Method
This method uses the obtained constructor information to actually create the object. You pass an object array containing the actual values in the order defined by the constructor definition. It returns the newly created instance.
3. Difference from Activator.CreateInstance
Activator.CreateInstance also exists as a simple way to create instances dynamically.
- Activator.CreateInstance: Convenient, but it infers the appropriate constructor internally, making strict control of overload resolution difficult in some cases.
- ConstructorInfo.Invoke: Since you perform a strict signature check with
GetConstructorbeforehand, it is suitable when you want to call a specific constructor precisely or when you prioritize performance by caching metadata.
