C# Activator.CreateInstance: Runtime Object Creation
Learn how to use C# Activator.CreateInstance to create objects at runtime, pass constructor arguments, handle exceptions, and evaluate performance tradeoffs.
Activator.CreateInstance is a static method in the System namespace. You can use it to create an object when the type is known only at runtime as a Type reference and no compile-time dependency on that type is needed. The examples in this article show the basic overloads, constructor argument handling, exceptions, and performance tradeoffs.
What Activator.CreateInstance Does
The simplest overload takes a Type object and returns a new instance of that type as an object. For example:
Type type = typeof(StringBuilder); object instance = Activator.CreateInstance(type);
This creates a StringBuilder instance. Because the method returns object, you usually need to cast it to the actual type before using its members. Additional overloads accept constructor arguments, binding flags, and culture information.
Creating Objects with Constructor Arguments
When the target type's constructor requires parameters, pass them to the overload that accepts constructor arguments. The runtime selects the constructor that best matches the supplied parameter types. For example:
Type type = typeof(Tuple<int, string>); object instance = Activator.CreateInstance(type, 42, "answer");
This creates a Tuple<int, string> with the values 42 and "answer". If the constructor is not public, use the overload that accepts BindingFlags. To invoke a private constructor:
object instance = Activator.CreateInstance(type, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { 1 }, CultureInfo.InvariantCulture);
The binding flags determine which constructors are considered. Without the correct flags, a private constructor can cause a MissingMethodException.
Handling Exceptions and Error Cases
Activator.CreateInstance can throw several exceptions. The most common ones are:
MissingMethodExceptionwhen no matching constructor is found.TargetInvocationExceptionwhen the constructor itself throws an exception.TypeLoadExceptionwhen the type cannot be loaded.
Catch these exceptions and handle them according to your application's needs. If the type might not have a parameterless constructor, you can check first with GetConstructor:
Type type = typeof(MyClass); ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes); if (ctor != null) { object instance = Activator.CreateInstance(type); } else { // Handle missing parameterless constructor }
This avoids throwing when the constructor is absent. For constructors with arguments, you can inspect GetConstructors() to find a suitable match.
Performance Considerations and Caching
Reflection-based creation is slower than direct instantiation because the runtime must resolve type metadata and invoke the constructor through reflection. The overhead matters when you create many instances in a hot path, but it may be negligible for occasional object creation.
One way to reduce repeated work is to cache the ConstructorInfo and use it to create new instances:
Type type = typeof(MyClass); ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes); Func<object> factory = () => ctor.Invoke(null);
Calling factory() still invokes the constructor through reflection, but it avoids repeated type metadata lookup. For greater speed, you can compile an expression tree into a delegate that calls the constructor directly, though this adds complexity. Measure and profile before optimizing; premature optimization can make the code harder to maintain without meaningful benefit.
Alternatives to Activator.CreateInstance
Depending on your scenario, other approaches may be more suitable:
- Generic constraints: If the type is known as a generic type parameter, use
new T()with awhere T : new()constraint. This provides compile-time type safety, but it only works when the type has a public parameterless constructor. - Dependency injection containers: They handle object creation with lifetime management and constructor injection, often using reflection internally and adding features such as interception and scoping.
RuntimeHelpers.GetUninitializedObject: Creates an object without calling any constructor, which can break invariants and is rarely used outside specialized scenarios.FormatterServices.GetUninitializedObject: Similar, used in serialization contexts.
The table below summarizes the tradeoffs:
| Approach | Type Safety | Constructor Args | Runtime Cost |
|---|---|---|---|
new T() | Compile-time | No (parameterless only) | Low |
| Activator.CreateInstance | Runtime | Yes | Medium |
| DI Container | Runtime | Yes | Depends on container |
When to Use Activator.CreateInstance
Use Activator.CreateInstance when you need to create an object from a Type known only at runtime and you do not already have a DI container or factory in place. Common scenarios include:
- Deserialization frameworks that instantiate types based on metadata.
- Plugin systems that load types from external assemblies.
- Test frameworks that create instances of test classes.
Avoid it when the type is known at compile time or when you can use a generic method with a new() constraint. Also avoid it in performance-critical loops unless you have measured and confirmed that the overhead is acceptable.
Common Pitfalls and Compatibility Issues
Do not assume that Activator.CreateInstance always finds a public parameterless constructor. Abstract types and interfaces throw a MemberAccessException when you try to instantiate them. Value types generally work, but a nullable value type with no value boxed to object becomes null.
Some reflection overloads differ across .NET versions, so check the documentation for your target framework. The basic behavior is the same, but the overload set may not be identical.
Activator.CreateInstance uses the type's default binder, which may not respect custom binding logic. If you need to resolve constructors with specific parameter types, use ConstructorInfo directly.
Finally, consider maintainability. Reflection-based creation makes code harder to refactor and debug. Prefer a factory interface or a generic method when possible, and isolate reflection-based creation behind a clear abstraction so the rest of your code does not depend on runtime type discovery.