Constructors

Constructors

Werking new operator

Objecten die je aanmaakt komen niet zomaar tot leven. Nieuwe objecten maken we aan met behulp van de new operator zoals we al gezien hebben:

Auto auto1 = new Auto();

De new operator doet 2 dingen:

  • Hij reserveert ruimte voor dit object in het geheugen

  • Hij zorgt ervoor dat initialisatiecode uitvoert om het object klaar te maken voor gebruik

Via de constructor van een klasse kunnen we code meegeven die moet uitgevoerd worden telkens een nieuw object van dit type wordt aangemaakt.

Constructors lijken erg op methodes. We zullen vaak zeggen dat het speciale methodes zijn, al bestaat daar wat technische discussie over. De constructor wordt in elk geval aangeroepen op een gelijkaardige manier aan een gewone methode. Daarom zetten we ronde haakjes: new Auto().

Soorten constructors

Als programmeur van eigen klassen zijn er 3 opties voor je:

  • Je gebruikt geen (expliciete) constructors: het leven gaat voort zoals het is. Je kunt objecten aanmaken zoals eerder getoond. Achter de schermen gebruik je wel een zogenaamde default constructor.

  • Je hebt enkel een parameterloze constructor nodig. Je kan nog steeds objecten met new Auto() aanmaken, maar je gaat zelf beschrijven wat er moet gebeuren bij de parameterloze constructor.

  • Je wenst gebruik te maken van een of meerdere constructoren met parameters. Hierbij zal je dan extra argumenten kunnen meegeven bij de creatie van een object, bijvoorbeeld: new Auto(25,25000). Dit kan bijvoorbeeld een auto maken met 25l benzine in de tank en 25000km op de teller. De betekenis van de getallen hangt af van hoe je de constructor schrijft.

Constructors zijn soms gratis, soms niet

Een lege default constructor voor je klasse krijg je standaard wanneer je een nieuwe klasse aanmaakt. Je ziet deze niet en kan deze niet aanpassen. Je kan echter daarom altijd objecten met new myClass() aanmaken.

Van zodra je echter beslist om zelf een of meerdere constructors te schrijven zal C# zeggen "Oké, jij je zin, nu doe je alles zelf". De default constructor die je gratis kreeg zal ook niet meer bestaan en heb je die dus nodig dan zal je die dus zelf moeten schrijven!

Default constructor

De default constructor is een constructor die je zelf niet schrijft. Hij heeft nooit parameters.

  • Standaard is hij public (meer info)

  • Heeft (zoals alle constructoren) als naam de naam van de klasse zelf

Stel dat we een klasse Auto hebben:

class Auto
{
    private int benzine = 5;
    private int kilometers;
}

We schrijven nergens code voor de constructor. Dan nog kunnen we new Auto() schrijven, maar dan wordt de auto aangemaakt met 5l in de tank en 0km (de defaultwaarde van int) op de teller.

Parameterloze constructor

We willen telkens een Auto-object wordt aangemaakt dat dit een random aantal kilometers op de teller heeft. Via een parameterloze constructor kunnen we dat oplossen (je kan namelijk alleen expressies gebruiken als initiële waarde).

Eerst schrijven de parameterloze constructor, deze ziet er als volgt uit:

class Auto
{
    private int benzine = 5;
    private int kilometers;

    public Auto()
    {
        // zet hier de code die bij initialisatie moet gebeuren
        // d.w.z. wat er moet gebeuren zodra de auto wordt gemaakt
    }
}

De constructor moet de naam van de klasse hebben, public zijn en geen returntype definiëren.

Vervolgens voegen we de code toe die we nodig hebben:

class Auto
{
    private int benzine = 5;
    private int kilometers;
    private static Random randomGen = new Random();

    public Auto()
    {
        // ook in de constructor kan je this gebruiken
        this.kilometers = randomGen.Next(0,200000);
    }
}

Telkens we nu een object zouden aanmaken met new Auto() zal deze een willekeurige kilometerstand hebben. Je kan trouwens ook in de constructor een initiële waarde aan benzine geven.

Zelfs als er een letterlijke initiële waarde wordt toegekend, gebeurt dit meestal in de constructor. Het is een kwestie van smaak, maar een constructor dient toch om te initialiseren.

Constructor met parameter(s)

Soms wil je argumenten aan een object meegeven bij creatie. We willen bijvoorbeeld de inhoud van de tank en de kilometerstand meegeven die het object moet hebben bij het aanmaken. Met andere woorden, stel dat we dit willen schrijven:

Auto auto1= new Auto(25,20000);

Als we dit met voorgaande klasse , die enkel een parameterloze (default) constructor heeft, uitvoeren, zal de code een fout geven. C# vindt geen constructor die twee ints als parameter heeft.

Net zoals bij overloading van methoden kunnen we ook constructors overloaden, d.w.z. alternatieven met dezelfde naam voorzien maar met een ander stel parameters. De code is gelijkaardig als bij method overloading:

class Auto
{
    int benzine;
    int kilometers;

    public Auto(int benzine, int kilometers)
    {
        this.benzine = benzine;
        this.kilometers = kilometers;
    }
}

Dat was eenvoudig. Maar denk eraan: je hebt een eigen constructor geschreven en dus heeft C# gezegd "ok, je schrijft zelf constructor, trek je plan. Maar de parameterloze versie zal je ook zelf moeten schrijven!" Je kan nu enkel je objecten met new Auto(int benzine, int kilometers) aanmaken. Schrijf je new Auto() dan zal je een error krijgen. Wil je die constructor nog hebben, dan zal je die met de hand moeten schrijven, bijvoorbeeld:

class Auto
{
    int benzine;
    int kilometers;

    public Auto(int benzine, int kilometers)
    {
        this.benzine = benzine;
        this.kilometers = kilometers;
    }

    public Auto() {
    }
}

Er is geen grens op het aantal constructoren dat je kan schrijven, als ze maar verschillende parameters hebben.

Wanneer heb ik constructoren nodig?

Tot recent maakten we onze objecten steeds aan met de default constructor. Pas daarna gaven we eventuele properties de juiste waarde. Dat houdt een risico in: er is een periode waarin onze objecten nog niet "af" zijn. In het slechtste geval vergeten we zelfs om de properties in te stellen en krijgen we objecten die misschien ongeldig zijn.

Constructoren helpen dit probleem te voorkomen. Als we één constructor hebben, bijvoorbeeld Auto(int benzine, int kilometers), moeten we die gebruiken. We kunnen dus niet vergeten bijvoorbeeld auto1.Benzine = 25 te schrijven, want we worden gedwongen meteen new Auto(25,20000) te schrijven.

Samengevat: als er eigenschappen zijn die je meteen bij het aanmaken van een object wil instellen, maak er dan parameters van een constructor voor.

Constructor chaining

Als je meerdere overloaded constructoren hebt, hoef je niet in elke constructor alle code voor objectinitialisatie te schrijven. Het sleutelwoordje this biedt ook de mogelijkheid eerst een andere constructor aan te roepen en eventueel andere operaties toe te voegen. Dit heet constructor chaining. In bovenstaand voorbeeld kan je ook dit schrijven:

class Auto
{
    private int benzine;
    private int kilometers;

    public Auto(int benzine, int kilometers)
    {
        this.benzine = benzine;
        this.kilometers = kilometers;
    }

    public Auto() : this(5,5)
    {
        // hier gebeurt niets meer
        // maar : this(5,5) zorgt dat een oproep van deze constructor
        // eerst de andere oproept, met beide waarden 5
    }
}

Last updated