ConstructorInfo - How To Make Reflection in DotNet Faster for Instantiation

Recently I wrote an article where I wanted to compare a popular way of creating object instances with DotNet reflection to another. In that article, I put Activator.CreateInstance head-to-head with Type.InvokeMember to see which had better performance. The result was Activator.CreateInstance in one specific case -- but another champion would emerge: ConstructorInfo.

In this article, I'll explain how you can get a ConstructorInfo reference using reflection in DotNet. I'll also expand upon the benchmarks from the previous article, showing you the code and how the results turned out.


What's In This Article: ConstructorInfo and Reflection in DotNet

Remember to check out these other platforms:

// FIXME: social media icons coming back soon!


Understanding ConstructorInfo From DotNet Reflection

In DotNet reflection, we get a powerful set of tools for inspecting assemblies, types, and members at runtime. One of the key components of this reflective capability is the ConstructorInfo class, which belongs to the System.Reflection namespace. This is where all of the goodies are -- Even the ones shown in this video that can be misused in the wrong hands:

ConstructorInfo allows developers to obtain information about the constructors of a class, including their accessibility (public, private, etc.), parameters, and metadata. But one of the best parts, which we'll be looking at in more detail, is that it enables the instantiation of objects dynamically at runtime without knowing their types at compile time.

And the best part? We're going to see that when we compare it to these other DotNet reflection mechanisms for making new instances, it's way faster.


Finding Constructors with Reflection in DotNet - Getting ConstructorInfo

In this section, we'll look at how we can first get ConstructorInfo instances so that we can leverage them later for object instantiation:

  1. Get the ConstructorInfo instances from types
  2. Find the right constructor
  3. ...
  4. Profit!

Something like that, right? Let's check these code examples out!

Get ConstructorInfo for All Public Constructors

To retrieve all public constructors of a class, you can use the GetConstructors method without any parameters by using a Type instance of a particular type. This method returns an array of ConstructorInfo objects representing each public constructor defined for the class.

Let's see it in action in this code example:

using System;
using System.Reflection;

public class SampleClass
{
    public SampleClass() { }
    public SampleClass(int i) { }
    protected SampleClass(string s) { }
    private SampleClass(int i, string s) { }
}

Type typeInfo = typeof(SampleClass);
ConstructorInfo[] publicConstructors = typeInfo.GetConstructors();

foreach (var constructor in publicConstructors)
{
    Console.WriteLine(constructor.ToString());
}

When we're thinking about dynamically invoking these things, it's likely going to be the case that we don't have the reference to the type though. But if we have the name of the type we're interested in, we can use the following:

Type typeOfInterest = Type.GetType("The.Namespace.Of.Your.Type.TheTypeName");

Get ConstructorInfo Including Private and Protected Constructors

To get information about all constructors, regardless of their accessibility level, you can use the GetConstructors method with the BindingFlags parameter. This approach allows you to include non-public constructors in the results:

ConstructorInfo[] allConstructors = typeInfo.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (var constructor in allConstructors)
{
    Console.WriteLine(constructor.ToString());
}

Keep in mind that if you watched the video I linked above, this starts to get into the territory of "Should I be doing this?". Please strongly consider if you need to be accessing non-public things -- Someone likely chose that access modifier for a reason.

Get ConstructorInfo Matching a Specific Signature

If you're looking for constructors that match a specific parameter signature, you can use GetConstructorInfo (notice that it's singular). This takes in binding flags like we saw before as well as an array of types that you want to match.

Here's how you could find constructors that take a single int parameter:

// Specify the parameter types of the 
// constructor you are looking for
Type[] paramTypes = new Type[] { typeof(int) };

// Use GetConstructor with the appropriate
// BindingFlags and parameter types
var constructor = typeInfo.GetConstructor(
    BindingFlags.Public | BindingFlags.Instance,
    paramTypes);

if (constructor != null)
{
    Console.WriteLine(constructor);
}
else
{
    Console.WriteLine("No matching constructor found!");
}

Note that this method will return null when there's no match, so ensure you got what you were looking for!


Creating Object Instances Using ConstructorInfo

Once we have a ConstructorInfo instance, we can start making object instances. This is what we'll be benchmarking in the upcoming sections!

In these examples, assume that we already have a ConstructorInfo instance called constructorInfo. We'd be getting this instance in any of the ways documented earlier in the article:

object instance = constructorInfo.Invoke(null);

The code above shows instantiating an object with a parameterless constructor. We pass in null for the list of arguments that would need to be provided -- because there are none. Take note that the type we get back is an object. If we have access to the type at compile, we could cast this instance to that type... But if we have access to the instance at compile time there are probably very few good reasons why you would be doing this in the first place. If you don't believe me, wait until you see the benchmark results.

If we want to instantiate using a constructor that takes parameters it would look like the following:

object instance = constructorInfo.Invoke(new object[] { 42 });

This code example shows a constructor with a single integer parameter defined being invoked with 42 as the single integer argument.


ConstructorInfo Performance Benchmarks

The moment you've all been waiting for! You might enjoy watching the video demonstrating these DotNet reflection benchmarks here:

BenchmarkDotNet Setup for Reflection Performance

Much like the previous article, I've just added a couple of additional scenarios for the ConstructorInfo scenarios. I wanted to mention that I added TWO scenarios for each class, and that's because I wanted to demonstrate the performance if you had to go instantiate AND find the ConstructorInfo back-to-back. I felt like this variation compared to already having the ConstructorInfo would be interesting to take note of.

Here is the full code, which you can also find on GitHub:

//
// This code was written for the following Dev Leader content:
// https://www.devleader.ca/2024/03/14/activator-createinstance-vs-type-invokemember-a-clear-winner/
// https://www.devleader.ca/2024/03/17/constructorinfo-how-to-make-reflection-in-dotnet-faster-for-instantiation/
// https://youtu.be/Djq7eMI_L-4 
//
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

using System.Reflection;

BenchmarkRunner.Run(Assembly.GetExecutingAssembly());
//BenchmarkSwitcher.FromAssembly(Assembly.GetExecutingAssembly()).RunAllJoined();

public class ParameterlessClass
{
}

public class ClassicStringParameterClass
{
    private readonly string _value;

    public ClassicStringParameterClass(string value)
    {
        _value = value;
    }
}

public class PrimaryConstructorStringParameterClass(
    string _value)
{
}

[ShortRunJob]
public class ParameterlessClassBenchmarks
{
    private Type? _type;
    private ConstructorInfo? _constructorInfo;

    [GlobalSetup]
    public void GlobalSetup()
    {
        _type = typeof(ParameterlessClass);
        _constructorInfo = _type.GetConstructor(Type.EmptyTypes);
    }

    [Benchmark]
    public void Constructor()
    {
        var instance = new ParameterlessClass();
    }

    [Benchmark(Baseline = true)]
    public void Activator_Create_Instance()
    {
        var instance = Activator.CreateInstance(_type!);
    }

    [Benchmark]
    public void Type_Invoke_Member()
    {
        var instance = _type!.InvokeMember(
            null,
            BindingFlags.CreateInstance,
            null,
            null,
            null);
    }

    [Benchmark]
    public void Constructor_Info_Invoke()
    {
        var instance = _constructorInfo!.Invoke(null);
    }

    [Benchmark]
    public void Find_Constructor_Info_Then_Invoke()
    {
        var constructorInfo = _type.GetConstructor(Type.EmptyTypes);
        var instance = constructorInfo!.Invoke(null);
    }
}

[ShortRunJob]
public class ClassicStringParameterClassBenchmarks
{
    private Type? _type;
    private ConstructorInfo? _constructorInfo;

    [GlobalSetup]
    public void GlobalSetup()
    {
        _type = typeof(ClassicStringParameterClass);
        _constructorInfo = _type.GetConstructor([typeof(string)]);
    }

    [Benchmark]
    public void Constructor()
    {
        var instance = new ClassicStringParameterClass("Hello World!");
    }

    [Benchmark(Baseline = true)]
    public void Activator_Create_Instance()
    {
        var instance = Activator.CreateInstance(
            _type!,
            new[]
            {
                "Hello World!",
            });
    }

    [Benchmark]
    public void Type_Invoke_Member()
    {
        var instance = _type!
            .InvokeMember(
                null,
                BindingFlags.CreateInstance,
                null,
                null,
                new[]
                {
                    "Hello World!",
                });
    }

    [Benchmark]
    public void Constructor_Info_Invoke()
    {
        var instance = _constructorInfo!.Invoke(new[]
        {
            "Hello World!",
        });
    }

    [Benchmark]
    public void Find_Constructor_Info_Then_Invoke()
    {
        var constructorInfo = _type.GetConstructor([typeof(string)]);
        var instance = constructorInfo!.Invoke(new[]
        {
            "Hello World!",
        });
    }
}

[ShortRunJob]
public class PrimaryConstructorStringParameterClassBenchmarks
{
    private Type? _type;
    private ConstructorInfo? _constructorInfo;

    [GlobalSetup]
    public void GlobalSetup()
    {
        _type = typeof(PrimaryConstructorStringParameterClass);
        _constructorInfo = _type.GetConstructor([typeof(string)]);
    }

    [Benchmark]
    public void Constructor()
    {
        var instance = new PrimaryConstructorStringParameterClass("Hello World!");
    }

    [Benchmark(Baseline = true)]
    public void Activator_Create_Instance()
    {
        var instance = Activator.CreateInstance(
            _type!,
            new[]
            {
                "Hello World!",
            });
    }

    [Benchmark]
    public void Type_Invoke_Member()
    {
        var instance = _type!
            .InvokeMember(
                null,
                BindingFlags.CreateInstance,
                null,
                null,
                new[]
                {
                    "Hello World!",
                });
    }

    [Benchmark]
    public void Constructor_Info_Invoke()
    {
        var instance = _constructorInfo!.Invoke(new[]
        {
            "Hello World!",
        });
    }

    [Benchmark]
    public void Find_Constructor_Info_Then_Invoke()
    {
        var constructorInfo = _type.GetConstructor([typeof(string)]);
        var instance = constructorInfo!.Invoke(new[]
        {
            "Hello World!",
        });
    }
}

ConstructorInfo Benchmark Results from BenchmarkDotNet

The first set of results will be for the parameterless constructor:

BenchmarkDotNet Results for DotNet Reflection - Comparing ConstructorInfo for Parameterless Constructors

In the results above, we clearly already knew that without using reflection, we get the best speed. No brainer here. BenchmarkDotNet says it's so fast it can't even measure it properly. But we'll notice that Activator.CreateInstance is technically a smidge faster here than using ConstructorInfo, even if we already had the instance ahead of time. The results are very close, and I have seen this swing the other way. So overall, these two are very comparable in this situation.

What happens if we need to use parameters though?

BenchmarkDotNet Results for DotNet Reflection - Comparing ConstructorInfo for a Constructor with One Parameter

The BenchmarkDotNet results above show that a classic style constructor taking in a single string parameter, ConstructorInfo is an order of magnitude faster than the other DotNet reflection options. Even if we need to look up the instance first, it's still almost twice as fast as the other options!

And of course, I wanted to see if primary constructors were any different in behavior:

BenchmarkDotNet Results for DotNet Reflection - Comparing ConstructorInfo for a Primary Constructor with One Parameter

Based on the results above though, they're right on par!


Wrapping Up ConstructorInfo and Reflection in DotNet

As we can see from the BenchmarkDotNet results, leveraging ConstructorInfo can be very performant! Only in the case where we're dealing with a public parameterless constructor did it seem to be right on par with Activator.CreateInstance. Technically in this run it showed that it was a touch slower, but I've run these before and seen the opposite case too. Overall, you'll want to consider if it makes sense for you to leverage this approach for creating object instances -- but certainly don't opt for reflection if you can easily just call new()!

If you found this useful and you're looking for more learning opportunities, consider subscribing to my free weekly software engineering newsletter and check out my free videos on YouTube! Remember to head over to the Discord community to chat with me and other like-minded software engineers!


Frequently Asked Questions: ConstructorInfo and Reflection in DotNet

What is ConstructorInfo in .NET?

ConstructorInfo is a class in the .NET framework that represents the constructor of a class or struct. It is part of the System.Reflection namespace and provides metadata about constructors, such as their parameters, accessibility (public, private, etc.), and allows for the dynamic instantiation of objects.

How can I use ConstructorInfo to create an instance of a class dynamically?

You can use the ConstructorInfo.Invoke method to create an instance of a class dynamically in .NET. This method requires an array of objects as arguments, which correspond to the parameters of the constructor you wish to invoke. For constructors without parameters, pass null or an empty array.

Can ConstructorInfo improve performance in .NET applications?

While using ConstructorInfo and reflection to dynamically instantiate objects offers flexibility, it can be slower than direct instantiation with the new keyword. However, techniques such as caching ConstructorInfo objects or using expressions can mitigate performance overhead and make reflection more efficient.

How do I get all constructors of a class using Reflection in DotNet?

To get all public constructors of a class, use the GetConstructors method without parameters on a Type object. To include non-public constructors (e.g., private, protected), specify BindingFlags such as BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance as arguments to GetConstructors.

How do I find a specific constructor using DotNet in .NET?

To find a specific constructor by its parameter types, use the GetConstructor method on a Type object, passing an array of Type objects representing the constructor's parameters. This method returns a single ConstructorInfo instance matching the signature, or null if no match is found.

What are the performance implications of using reflection in .NET?

Reflection in .NET can introduce performance implications due to the overhead of type inspection and dynamic method invocation. However, these can be minimized through optimization strategies like caching, using lightweight reflection alternatives, or avoiding reflection in performance-critical paths.

Is it possible to use ConstructorInfo with private constructors?

Yes, ConstructorInfo can be used to invoke private constructors of a class. This requires obtaining the ConstructorInfo object with the appropriate BindingFlags to include non-public members and then calling the Invoke method. This technique should be used judiciously, respecting the encapsulation intended by the class design.

How to Master the Art of Reflection in CSharp and Boost Your Programming Skills

Learn about reflection in CSharp and how it can be used. See how reflection in C# allows you to explore and modify objects, classes, and assemblies at runtime.

Reflection in C#: 4 Simple But Powerful Code Examples

Reflection in C# is powerful, but with great power comes great responsibility. Check out these 4 quick examples of reflection in C# to see it in action!

Activator.CreateInstance in C# - A Quick Rundown

Leverage Activator.CreateInstance in C#, part of reflection in C#, to create object instances! This beginner's guide walks you through simple C# code examples.

An error has occurred. This application may no longer respond until reloaded. Reload x