Instantiating an object without using its constructor in C#

Β· 911 words Β· 5 minutes to read

In this blog post we will look at how you could create an instance of an object in C# without calling its constructor - which sounds a bit crazy, but is actually something that has been supported by both CLR and Core CLR for a long time now.

More after the jump.

The language spec πŸ”—

The C# language specification is quite clear in terms of what is required to instantiate an object. It states that you must use the instance constructor.

10.11 Instance constructors

An instance constructor is a member that implements the actions required to initialize an instance of a class.

This is quite obvious if you are C# developer, right? We have always used the language this way - if you have some work to be run at object creation, we put it in the instance constructor. This is very important as it guarantees certain level of consistency and safety in our code - especially as we share code with other teams and developers.

Example πŸ”—

This example is adapted from some real life code I encountered recently. It’s of course super-simplified to allow us to focus on the important concepts that are the theme of this blog, but the gist of this example, nevertheless, comes from a real world usage.

Imagine you have a library that deals with tax calculation - in this particular case, calculating the value-added tax.

    public class TaxCalculator
    {
        public double CalculateVAT(double price)
        {
            return Math.Round(price * 0.22, 2);
        }
    }

Now, imagine you distribute this library to other developers, and you require a license to be used in other for the library to function. You might think, OK, let’s rely on the constructor to validate the license - after all no one will be able to instantiate your calculator without going through the constructor.

So you add some code like this, that will validate the license in the constructor of your TaxCalculator.

public class TaxCalculator
{
    public TaxCalculator(string license)
    {
        ValidateLicense(license);
    }

    // dummy validation for illustration only
    private void ValidateLicense(string license)
    {
        if (string.IsNullOrEmpty(license))
        {
            throw new ArgumentException("Your license is invalid, you stupid developer", nameof(license));
        }
    }

    public double CalculateVAT(double price)
    {
        return Math.Round(price * 0.22, 2);
    }
}

Now you are happy because each user of this code has to present a valid license upon object construction. By the way, let’s take a short pause here - the code used here is purely to illustrate the concept of bypassing the constructor. It is not a tutorial on how to do licensing! It’s an example, and we should not read too much into it.

Anyway, now our users can use the TaxCalculator in the following way, and if you have a valid license, everything works fine:

var calculator = new TaxCalculator("valid_license");
Console.WriteLine(calculator.CalculateVAT(11.99));
// prints 2.64

Bypassing the constructor πŸ”—

Unfortunately, by putting the validation and integrity/sanity check into the constructor, we fell into a small trap. Turns out, it is actually possible (and officially supported) to create an object instance in .NET without running its instance constructor. In fact, it’s been there since .NET 1.1.

The relatively unknown API is called FormatterServices.GetUninitializedObject and it will allocate an object without running any initalization code, including the instance constructor. The object is completely uninitialized, which also means that, for example, members would not be initialized to their values.

There is a sibling API in the System.Runtime.CompilerServices namespace and it’s called RuntimeHelpers.GetUninitializedObject; it was only added in .NET Core 2.0 and is available in .NET Standard 2.1. The former API (FormatterServices) actually ends up calling the latter (CompilerServices) internally on .NET Core.

Specifically on Core CLR, you can have a look at the implementation of this creation of uninitialized object in the native code here. If you follow that code, you’d see that the Core CLR basically only checks against a few restrictions such as if the type is generic or abstract, and then ends up allocating an object and returning it, completely bypassing constructors.

Afterwards you are free to use it. For example, the following code is perfectly valid:

var calculator = RuntimeHelpers.GetUninitializedObject(typeof(TaxCalculator)) as TaxCalculator;
Console.WriteLine(calculator.CalculateVAT(11.99));
// prints 2.64

It works the same way as previously, and we end up getting same result. The difference is, of course, that we ended up bypassing the constructor and avoided the check we put there (license check) in the first place.

Another interesting aspect of this is that you can use the same technique to create instances of objects that only have private constructors. This is typical in builder patterns. For example, given the following:

    public class Dog
    {
        private Dog() { }

        // factory method to be used instead of constructor
        public static Dog CreateDog()
        {
            // some validation on how Dog must be created
            return new Dog();
        }

        public string Bark() => "woof!";
    }

You could just use GetUninitializedObject APIs and bypass the factory method completely:

var dog = RuntimeHelpers.GetUninitializedObject(typeof(Dog)) as Dog;
Console.WriteLine(dog.Bark());
// prints woof!

Summary πŸ”—

These GetUninitializedObject are actually used quite widely in serialization libraries and frameworks. The lesson here, however, is that when designing your own code that will be consumed by other developers, don’t always assume that they will go through the constructor - sometimes moving a certain check to a different place (i.e. from constructor to a specific method) can help avoid certain misbehavior or prevent others from doing something stupid!

There is really not much there, but if you are interested, the demo code for this article is available on Github.

About


Hi! I'm Filip W., a software architect from ZΓΌrich πŸ‡¨πŸ‡­. I like Toronto Maple Leafs πŸ‡¨πŸ‡¦, Rancid and quantum computing. Oh, and I love the Lowlands 🏴󠁧󠁒󠁳󠁣󠁴󠁿.

You can find me on Github, on Mastodon and on Bluesky.

My Introduction to Quantum Computing with Q# and QDK book
Microsoft MVP