-1

I recently came across someone who mentioned we should not initialize class variables (values from https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/default-values-table) if we are going to initialize it with default value. It sounds very wrong to me. Is there any actual performance gain with this? In below example he doesn't want to set flag as false initially (although assume it to be false for scenario).

Class Test {
     private bool flag;
     private int CheckValue()
     {
          if(flag) return 0;
          else return 1;
     }
     private void SetFlag()
     {
         flag = true;
     }
}
user2492339
  • 191
  • 1
  • 4

3 Answers3

1

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.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • While I'm not quite sure what the OP was on about, your answer does the best to explain the performance impact. Thank you. – brenwebber Jan 12 '23 at 14:44
0

C# doesn't allow use of undefined variables. The CLR still initializes to default. Not setting a variable when possible taps into being able to make this-will-be-set-later assertions being checked by the compiler

OTOH, fields don't have such checking. When setting the default values to default values, this can cause a nuisance with the debugger stepping through setting all these fields to those default values. Not setting them there can be a QoL improvement wrt debugging experience

Demur Rumed
  • 421
  • 6
  • 17
  • 2
    `C# doesn't allow use of undefined variables.` That's not true. *Certain* variables need to be initialized (i.e. locals), but not all. – Servy Aug 25 '17 at 19:48
  • Here is a good answer on why locals are not allowed to be undefined, but class variables are: https://stackoverflow.com/a/30820127/2193107 It primarily has to do with the complexity of detecting whether something is actually unassigned. – JBC Aug 25 '17 at 19:53
0

In the case you've identified you are referring to a bool field which is a value object under the covers and can NOT be null. It is set to "false" by default.

The default value of a bool variable is false.

Source : learn.microsoft.com

P. Roe
  • 2,077
  • 1
  • 16
  • 23