If you’ve ever worked with reflection in .NET, you’re likely familiar with Activator
. In .NET 6,
ActivatorUtilities
was introduced to make it easier to create classes with dependencies. Constructor dependency injection is common in the .NET space, and you’re likely to run into several types that require constructor parameters before being created. The
Activator
class is a relic of a simpler time, whereas ActivatorUtilities
meets developers where .NET is today.
In this post, we’ll look at a code example of how to guide
ActivatorUtilities
toward the constructor you want to use when building instances of types that may have multiple constructors.
Registering a Type and the Dependencies
Let’s start by looking at a Person
class that has a total of three constructors. Wowza!
public class Person(string name, int age = 0)
{
public Person(string name)
: this(name, 0)
{
Console.WriteLine("Person(string)");
}
public Person(object name)
: this(name.ToString()!, 41)
{
Console.WriteLine("Person(object)");
}
public string Name { get; } = name;
public int Age { get; } = age;
}
We have the following constructors.
- A primary constructor that takes a name and an optional age
- A constructor that takes a
string
dependency - A constructor that takes an
object
dependency
Let’s set up our new
ServiceCollection
and add dependencies to the dependency collection. Note that you’ll need to install the NuGet package
Microsoft.Extensions.DependencyInjection
if you haven’t already.
using Microsoft.Extensions.DependencyInjection;
var collection = new ServiceCollection();
var value = "Khalid";
collection.AddTransient<Person>();
// an instance of object
collection.AddSingleton<object>(value);
// an instance of string
collection.AddSingleton<string>(value);
var serviceProvider = collection.BuildServiceProvider();
var person = ActivatorUtilities.CreateInstance<Person>(serviceProvider);
Console.WriteLine($"{person.Name} is {person.Age} years old.");
Take a second and guess what constructor implementation is called. We’ve satisfied all the dependencies, but just by looking at the code, you can feel a sense of uncertainty creeping in. Let’s run it.
Khalid is 0 years old.
Ah, the primary constructor got called. It makes sense; that’s what “primary” means, right?
What if we want a different constructor to get called?
Using the ActivatorUtilitiesConstructor Attribute
In the Microsoft.Extensions.DependencyInjection
package, you’ll find the
ActivatorUtilitiesConstructorAttribute
. This attribute gives the
ServiceProvider
hints at which constructor to call first. Let’s modify our
Person
class definition to call the constructor with the object
dependency.
public class Person(string name, int age = 0)
{
public Person(string name)
: this(name, 0)
{
Console.WriteLine("Person(string)");
}
[ActivatorUtilitiesConstructor]
public Person(object name)
: this(name.ToString()!, 41)
{
Console.WriteLine("Person(object)");
}
public string Name { get; } = name;
public int Age { get; } = age;
}
After rerunning our code, we’ll get the following output:
Person(object)
Khalid is 41 years old.
Note that we did not change the executing code; we only added a single attribute. With this knowledge, you can now have all the constructors you want on your types but still make it clear which constructor you’d like
ActivatorUtilities
to use.
There you have it. I hope you enjoyed this post. As always, thanks for reading and sharing with friends and colleagues. Cheers.