Virtual member call in a constructor: when the C# compiler warns, and it's not for nothing
Every C# developer who has ever written classes with inheritance has probably seen this ReSharper or Visual Studio warning: «Virtual member call in a constructor». It looks harmless, but in reality, it's a red flag that can lead to hard-to-find bugs and performance degradation. Today we'll break down what lies behind this warning, how it relates to performance, and why even experienced programmers sometimes make this mistake.
This topic isn't pulled out of thin air: the question «Virtual member call in a constructor» on Stack Overflow has garnered thousands of views and dozens of answers. This means the problem is real and affects many. But we'll go further: not just explain how to fix it, but show how to optimize code, avoiding unnecessary allocations and unexpected behavior.
What is «Virtual member call in a constructor»?
Imagine a situation: you create a base class, and in its constructor, a virtual method is called. What could go wrong, you might think? After all, virtual methods are designed to be overridden. But the problem is that when a virtual method is called from the base class constructor, the derived class object is not yet fully initialized. This can lead to the overridden method being called on a "raw" object, where fields are not yet set and dependencies are not injected.
public class BaseClass{ public BaseClass() { // Warning! Virtual call in constructor VirtualMethod(); }
public virtual void VirtualMethod() { Console.WriteLine("BaseClass.VirtualMethod"); }}
public class DerivedClass : BaseClass{ private readonly string _data;
public DerivedClass(string data) { _data = data; }
public override void VirtualMethod() { // _data is not yet initialized! Console.WriteLine($"DerivedClass: {_data?.Length ?? 0}"); }}
// Usagevar obj = new DerivedClass("Hello"); // Outputs: DerivedClass: 0, not 5In this example, _data will be null at the time VirtualMethod is called, because the derived class constructor hasn't executed yet. This is a classic case where a virtual call in a constructor leads to non-obvious behavior.
Why is this a performance problem?
At first glance, this is just a logical error. But let's dig deeper. When you call a virtual method from a constructor, the runtime (CLR) has to perform additional checks and dispatch. In a normal situation, the virtual method table (vtable) is already ready, and the call happens quickly. However, in the base class constructor, the vtable may not yet be fully built for the derived type. This leads to:
- Additional dispatch overhead — the CLR spends time checking the validity of the call.
- Potential JIT compilation of suboptimal code — the JIT may not be able to inline such a call, reducing performance.
- Extra null checks — if you override the method, you have to write defensive code, which increases IL size and execution time.
In high-load systems where constructors are called thousands of times per second, these micro-delays can accumulate. Therefore, C# code optimization should start with such seemingly small details.
When can a virtual call in a constructor be useful?
Spoiler: almost never. But there are rare exceptions when you know exactly what you're doing. For example, in the Template Method pattern, where the base class defines the skeleton of an algorithm, and derived classes