I recently came across someone who mentioned we should not initialize class variables … if we are going to initialize it with default value.
I agree, but…
Is there any actual performance gain with this?
No, it's exactly the same. The difference is a matter only of style.
Let's take a closer look though. Let's have variants of your class that don't set your flag, initialise it to the default, initialise it otherwise, and which do both in the constructor:
public class NoInit
{
private bool _flag;
}
public class DefaultInit
{
private bool _flag = false;
}
public class NonDefaultInit
{
private bool _flag = true;
}
public class TestDefaultCtor
{
private bool _flag;
public TestDefaultCtor()
{
_flag = false;
}
}
public class TestNonDefaultCtor
{
private bool _flag;
public TestNonDefaultCtor()
{
_flag = true;
}
}
Now lets compile release builds:
.class public auto ansi beforefieldinit NoInit
extends [mscorlib] System.Object
{
.field private bool _flag
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib] System.Object::.ctor()
IL_0006: ret
}
}
.class public auto ansi beforefieldinit DefaultInit
extends [mscorlib] System.Object
{
.field private bool _flag
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib] System.Object::.ctor()
IL_0006: ret
}
}
.class public auto ansi beforefieldinit NonDefaultInit
extends [mscorlib] System.Object
{
.field private bool _flag
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: stfld bool NonDefaultInit::_flag
IL_0007: ldarg.0
IL_0008: call instance void [mscorlib] System.Object::.ctor()
IL_000d: ret
}
}
.class public auto ansi beforefieldinit TestDefaultCtor
extends [mscorlib]System.Object
{
.field private bool _flag
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ldarg.0
IL_0007: ldc.i4.0
IL_0008: stfld bool TestDefaultCtor::_flag
IL_000d: ret
}
}
.class public auto ansi beforefieldinit TestNonDefaultCtor
extends [mscorlib]System.Object
{
.field private bool _flag
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ldarg.0
IL_0007: ldc.i4.1
IL_0008: stfld bool TestNonDefaultCtor::_flag
IL_000d: ret
}
}
As you can see, while setting the flag to true has an effect (well, it has to happen somewhere) and setting it in the constructor does result in the setting to false being explicitly compiled (though, that does not necessarily mean there is any difference once jitted), between DefaultInit
and NoInit
there is no difference in what it is compiled to. If we were to decompile it back to C# we'd have to make our own call as to explicitly set it to false or not just as when writing it in the first place.
So in terms of results the two are exactly the same.
This doesn't make the difference completely unimportant. (Thought I'd rather a project I was working on was consistently using the approach I don't like than being very inconsistent about it; inconsistency is worse).
But, lots of initialisations can be a bit busy and don't add any information* to a coder experienced enough to know what types' defaults are and make it clearer when we are initialising to a particular value for a reason.
It can also seem particularly redundant if we're going to (or sometimes going to) set the value in a constructor anyway. (Though note, initialising to default an then setting in a constructor is compiled no different than just setting in a constructor, though the same is not true if we initialise to a non-default—something that will often be optimised away when jitting, but not always, especially when the initialisation has side-effects).
It can also lead to a lower signal-to-noise during debugging session to have lots of uninteresting initialisations happening.
So, I think it's a reasonable thing to have coding conventions about, and I agree with the person you mention as to what I would have those conventions be, but they're conventions, not something that impacts on performance.
*In contrast using private
or internal
where those are the default at least adds the information of whether one is in a class or namespace context respectively (which is what makes those the defaults, respectively]), so it's one little thing less for the reader to think about. So I prefer that to be explicit.