Initialize private fields in the declaration or constructor?

Asked

Viewed 722 times

8

I’m modeling a class that has a private list and an internal dependency on another object:

public class Teste
{
    private IList<string> Textos;
    private Teste2 Teste2;
}

I can initiate them in the statement:

private IList<string> Textos = new List<string>();
private Teste2= new Teste2();

Or via builder:

public Teste()
{
    Textos = new List<string>();
    Teste2 = new Teste2();
}

Which of the two approaches is recommended? It’s just a matter of coding style or one of them hurts some principle of object orientation?

  • 3

    I don’t know if in c# it makes a difference, but in java it’s the same thing. If I’m not mistaken, even starting in the declaration, the compiler starts inside the default constructor. Referent: http://stackoverflow.com/a/1994232/5524514

3 answers

10


Actually there is no way the field can be initialized by itself, it needs a code to do this. And codes can only be placed in methods. If it is a field being initialized a method in the construction needs to be executed by initializing. So the boot in the field is an illusion, in fact it is played in the constructor even if you don’t see it like that. So it’s the same.

See how it’s the same.

With field initialization:

.method public hidebysig specialname rtspecialname 
    instance void .ctor () cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 18 (0x12)
    .maxstack 8

    IL_0000: ldarg.0              // Load argument 0 onto the stack
    IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor() // Allocate an uninitialized object or value type and call ctor
    IL_0006: stfld class [mscorlib]System.Collections.Generic.IList`1<string> Teste::Textos // Replace the value of field of the object obj with value
    IL_000b: ldarg.0              // Load argument 0 onto the stack
    IL_000c: call instance void [mscorlib]System.Object::.ctor() // Call method indicated on the stack with arguments
    IL_0011: ret                  // Return from method, possibly with a value
} // end of method Teste::.ctor

With initialization in the constructor:

.method public hidebysig specialname rtspecialname 
    instance void .ctor () cil managed 
{
    // Method begins at RVA 0x2063
    // Code size 18 (0x12)
    .maxstack 8

    IL_0000: ldarg.0              // Load argument 0 onto the stack
    IL_0001: call instance void [mscorlib]System.Object::.ctor() // Call method indicated on the stack with arguments
    IL_0006: ldarg.0              // Load argument 0 onto the stack
    IL_0007: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor() // Allocate an uninitialized object or value type and call ctor
    IL_000c: stfld class [mscorlib]System.Collections.Generic.IList`1<string> Teste2::Textos // Replace the value of field of the object obj with value
    IL_0011: ret                  // Return from method, possibly with a value
} // end of method Teste2::.ctor

Note that the compiler always creates a constructor even if you don’t declare it. And he puts the base class constructor call inside the constructor. He does this as soon as possible, inside the builder. In case of having fields with initializers this is placed inside the builder before the call of the base constructor.

It is important to note that probably the builder of Object will disappear since it is empty and the Jitter should eliminate it.

You can see more about this at New C# 6 functionality "Auto-Property initializers" is just a facilitator?.

Understand What good is a builder? and why you should use it or not.

I tend to avoid the builder whenever possible. You have to use what else makes sense to that case.

In newer versions it is possible to use initialization in property.

In C# 9 it is even possible to parameterize the creation of an object only with the initialization via property, but it is another matter. see more in https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/init?WT.mc_id=DOP-MVP-5002397

3

For most cases, either initialize in the declaration or constructor, if you want to give a default value. The object only exists in memory even at the time it is built.

If you want to be very pedantic, you can debug the construction of the object and you will notice that the properties initialized in the declaration receive values before the start of the constructor method you declared. But that doesn’t have much practical effect.

Regarding good practices: each case is a case, so instead of discussing which form is most recommended, it is more useful to know the advantages and disadvantages of each form.

Declaring values in the constructor has the advantage that things tend to be concentrated at a single point, or at least at a few points in the case of multiple constructors. Compare:

public class Foo
{
    public Foo()
    {
        this.A = new {};
        this.B = new {};
        this.C = new {};
        this.D = new {};
        // etc., etc.
        this.Z = new {};
    }
}

With:

public partial class Foo // Atenção especial para o partial
{
    // 26 propriedades

    public Foo()
    {
        /* Seus colegas perguntaram qual era o valor padrão da propriedade Y
         * e você veio procurar aqui. E você provavelmente abriu este código
         * no github e não em uma IDE. Vou dar uma dica, a declaração está
         * em outro arquivo. Descobrir qual será o seu desafio. Compartilhe
         * se você encontrar em até 60 segundos.
         */

         // Sério, não tem código aqui. Este é um construtor vazio.
    }
}

Already initializing in the statement can have its advantages there if you are working with properties. You can do Lazy Loading ("lazy load"), as follows:

private List<string> _afazeres;
public List<string> Afazeres
{
    get
    {
        if (this._afazeres == null)
        {
            this._afazeres = new List<string>()
            {
                "Programar",
                "Postar no Stack Overflow",
                "Estudar",
                "Beber café até morrer",
                "Fazer tudo de novo"
            }
        }
        return this._afazeres;
    }
    set { this._afazeres = value; }
}

This ensures that your class can be instantiated without allocating memory to any field - that field will only take up space from the first time it is accessed. Here we have a short list of strings, but this could be some really heavy object.

-2

Advantage of Initializing in Declaration

As has been said, "Initializing in the Declaration" is not really what happens, but I will use that term in this answer. If you initialize in the Declaration it is easier to know what is in the variable when the instance is created, because you just find the Variable Declaration.
This is the Simplest Solution.


With Builders you can do more

With a Constructor you can define the contents of the variable dynamically in instance creation, through different constructors that assign different things to the variable and/or through constructor parameters that will be used to define the variable value.

Therefore, Constructors give you greater flexibility when defining the variable content when the instance is created, but, increase the Complexity.


Disadvantages of using Constructors

  • If you have multiple Constructs, each one set a different value for the variable, you will need to know which Constructor was used to create the Instance to know what is in the variable when the Instance was created. This makes debug difficult.

  • If the value of the variable is defined through a Parameter in a Constructor, you will need to know what was passed by this Parameter to know what is in the variable when the instance was created. This also makes debug difficult.

  • The complexity of the project and the amount of code increases when declaring Constructors, this can make your project more difficult to understand (as there will be more code to study).


Completion

Use Constructors only when it’s really necessary, otherwise you will be creating Unnecessary Complexity. If you know you won’t need the Constructor(s) you’re thinking of creating, don’t create them, Initialize in the Declaration.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.