Overerving
In de vorige sectie heb je gekeken naar het concept van afgeleide klassen. Deze eigenschap van een klasse om eigenschappen van een andere klasse over te nemen, wordt overerving genoemd.
Hoewel je het concept van overerving al kent, wordt het deze keer op een iets uitgebreidere manier behandeld om het grondiger te begrijpen.
Ter herziening volgt hier een voorbeeld van overerving:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kilogram (1 kg = 2.2 pounds) } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Cat myCat = new Cat(); Dog myDog = new Dog(); myCat.speak(); myDog.speak(); } }
De directive #pragma warning disable "warning code"
kan worden gebruikt om bepaalde compilerwaarschuwingen uit te schakelen. Het is over het algemeen af te raden om waarschuwingen uit te schakelen, omdat het negeren ervan tot onverwacht gedrag in het programma kan leiden.
De bovenstaande code bevat één bovenliggende klasse genaamd Mammal
en twee afgeleide klassen genaamd Cat
en Dog
.
Let op dat geen van de klassen expliciet een constructor heeft gedefinieerd, wat betekent dat deze klassen een standaardconstructor zullen gebruiken wanneer een object wordt aangemaakt.
Een standaardconstructor wordt automatisch door de programmeertaal geleverd als een klasse geen expliciet gedefinieerde constructors heeft. Een standaardconstructor is in feite een lege constructor die geen code bevat, bijvoorbeeld public className() {}
is hoe een standaardconstructor eruit kan zien. Omdat deze geen attributen expliciet initialiseert, krijgen alle attributen standaardwaarden - ook wel nulwaarden genoemd.
Laten we handmatig een constructor maken voor de klasse Mammal
, die een Mammal
-object initialiseert met bepaalde gegevens:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
Als u probeert dit programma te compileren, zullen er enkele fouten in de console verschijnen. Om deze fouten te begrijpen, moet u eerst twee belangrijke concepten met betrekking tot constructors begrijpen.
Het eerste concept is dat zodra u expliciet een constructor voor een klasse definieert, die klasse geen standaardconstructor meer heeft. De expliciet gedefinieerde constructor wordt dan de hoofdconstructor van die klasse, wat in dit geval het volgende betekent:
index.cs
12345public Mammal(int age, float weight) { this.age = age; this.weight = weight; }
Daarom moeten bij het aanmaken van een nieuw object altijd de vereiste argumenten van de constructor in de juiste volgorde worden doorgegeven:
index.cs
1234567// Incorrect ways to create 'Mammal', will show an error Mammal m1 = new Mammal(); Mammal m1 = new Mammal(10); Mammal m1 = new Mammal(42.0f); // Correct way to create 'Mammal', will execute fine. Mammal m1 = new Mammal(10, 42.0f);
Ten tweede kunnen afgeleide klassen ook constructors hebben; echter, voordat de constructor van een afgeleide klasse wordt aangeroepen, wordt eerst de constructor van de basisklasse (ouder) aangeroepen:
index.cs
1234567891011121314151617181920212223242526272829303132333435#pragma warning disable CS0169 // To disable some unnecessary warnings, using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg // No attribute is initialized explicitly in this constructor // Hence, all attributes will take up "zero" values // It is similar to a "default" constructor except it outputs a message public Mammal() { Console.WriteLine("Mammal Constructor Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog Constructor Called"); } } class ConsoleApp { static void Main() { Dog myDog = new Dog(); } }
Wanneer je deze code uitvoert, zie je dat de WriteLine()
-methode uit de constructor van 'Mammal', de bovenliggende klasse, automatisch wordt aangeroepen. Dit betekent dat het een regel is dat de constructor van de basisklasse (ook wel de base constructor genoemd) altijd wordt aangeroepen vóór de constructor van de afgeleide klasse.
Deze regel geldt ook bij meerlagige overerving:

In het bovenstaande diagram roept de Kitten
-constructor eerst de Cat
-constructor aan, maar omdat Cat
ook een afgeleide klasse is, roept deze eerst de Mammal
-constructor aan, en Mammal
roept op zijn beurt de Animal
-constructor aan voordat zijn eigen constructor wordt uitgevoerd. Hierdoor is de eerste constructor die wordt uitgevoerd die van de superklasse — namelijk de constructor van de Animal
-klasse, waarna het verder naar beneden gaat.
Als de constructor van de bovenliggende klasse geen argumenten vereist, wordt deze automatisch door de compiler aangeroepen. Dit is de reden waarom de 'Mammal'-constructor in het bovenstaande voorbeeld automatisch werd aangeroepen. Laten we echter opnieuw naar de foutieve code kijken:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
In de bovenstaande code treden er twee fouten op die in feite betekenen dat de base constructors niet handmatig zijn aangeroepen — omdat deze enkele argumenten vereisen, moet je deze handmatig aanroepen. De basis-syntaxis voor het handmatig aanroepen van de constructor van de bovenliggende klasse is als volgt:
index.cs
12345678910class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }
Voorbeeld:
index.cs
1234567891011121314151617181920212223242526272829303132using System; class ExampleParentClass { int value1; int value2; public ExampleParentClass(int value1, int value2) { this.value1 = value1; } } class ExampleDerivedClass : ExampleParentClass { int value3; // The value1 and value2 arguments are passed to the base class's contructor public ExampleDerivedClass(int value1, int value2, int value3) : base (value1, value2) { this.value3 = value3; } } class ConsoleApp { static void Main() { var testObject = new ExampleDerivedClass(5, 7, 9); } }
Met deze syntaxis kun je alle vereiste gegevens doorgeven aan de Mammal
constructor via de Cat
en Dog
constructors om de fout die je eerder kreeg op te lossen:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public Cat(int age, float weight, string furPattern) : base(age, weight) { this.furPattern = furPattern; } public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); // Creating a "Dog" object with some data Dog d1 = new Dog(10, 42.5f, "Dobermann"); Console.WriteLine("Executed Successfully"); } }
Een andere belangrijke eigenschap van constructors is dat je constructors kunt overbelasten, net zoals we elke andere methode kunnen overbelasten. Je kunt meerdere constructors maken met een verschillend aantal argumenten:
index.cs
12345678910111213141516171819202122232425class Mammal { int age; float weight; // kg // 1st constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }
In dit geval heeft de klasse Mammal
3 constructors. Hierdoor kun je een mammal-object op 3 verschillende manieren initialiseren of aanmaken, en de compiler kiest welke constructor wordt aangeroepen op basis van het aantal en type argumenten:
index.cs
1234// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);
Dit betekent ook dat je een van de drie constructors kunt aanroepen vanuit de constructors van de afgeleide klasse. Bijvoorbeeld, al deze zijn geldig:
index.cs
123456789101112131415161718// Using 3rd base constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } // Using 2nd base constructor public Dog(int age, string breed) : base(age) { this.breed = breed; } // Using 1st base constructor // If the base constructor has no arguments then it is automatically called (similar to the default constructor), so we don't necessarily need to write 'base()' public Dog(string breed) { this.breed = breed; }
Laten we de bovenstaande twee codefragmenten samenvoegen en enkele Console.WriteLine
-instructies toevoegen om te zien in welke volgorde de constructors worden uitgevoerd, zodat we het resultaat praktisch kunnen waarnemen:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394using System; class Mammal { int age; float weight; // kg // 1st Constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor // The attributes are initialized with zero values Console.WriteLine("Mammal - Constructor 1 Called"); } // 2nd Constructor public Mammal(int age) { this.age = age; Console.WriteLine("Mammal - Constructor 2 Called"); } // 3rd Constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; Console.WriteLine("Mammal - Constructor 3 Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog - Constructor 1 Called"); } // Using 1st Mammal constructor // We don't necessarily need to write 'base()' in this case // It automatically finds and calls the constructor with no arguments public Dog(string breed) { this.breed = breed; Console.WriteLine("Dog - Constructor 2 Called"); } // Using 2nd Mammal constructor public Dog(int age, string breed) : base(age) { this.breed = breed; Console.WriteLine("Dog - Constructor 3 Called"); } // Using 3rd Mammal constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; Console.WriteLine("Dog - Constructor 4 Called"); } public void speak() { Console.WriteLine("Woof!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object using different constructors Mammal m1 = new Mammal(10, 42.0f); Mammal m2 = new Mammal(10); Mammal m3 = new Mammal(); Console.WriteLine("----------"); // Seperator, for ease of reading output // Creating a "Dog" object using different constructors Dog d1 = new Dog(10, 42.0f, "Dobermann"); Console.WriteLine(""); Dog d2 = new Dog(10, "Dobermann"); Console.WriteLine(""); Dog d3 = new Dog("Dobermann"); Console.WriteLine(""); Dog d4 = new Dog(); } }
Nu je bekend bent met verschillende kenmerken van overerving, is het ook belangrijk te weten hoe en wanneer je deze correct toepast. Houd rekening met de volgende aandachtspunten bij het overwegen van een op overerving gebaseerde klassenstructuur:
Balans tussen eenvoud en flexibiliteit: Constructor-overloading maakt het mogelijk om meerdere verschillende constructors te hebben die verschillende typen argumenten accepteren, maar overmatig gebruik kan de code complex en moeilijk te onderhouden maken. Het is een best practice om de klassecode kort, bondig en overzichtelijk te houden. Vermijd het maken van te veel constructors voor een klasse om een balans tussen eenvoud en flexibiliteit te behouden.
Houd constructors eenvoudig: Constructors dienen voornamelijk verantwoordelijk te zijn voor het initialiseren van een object met basisgegevens. Het is een best practice om onnodige verwerking en complexe logica binnen een constructor te vermijden. Als er berekeningen of logica nodig zijn, is het beter hiervoor een aparte methode te maken.
;Slechte praktijk:;
index.cs
123456789101112131415161718192021222324252627class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; if (accountType == "Savings") { // Plus 1 Percent this.balance = balance + balance * 0.01; } else if (accountType == "HighYieldSavings") { // Plus 5 percent this.balance = balance + balance * 0.05; } else { this.balance = balance; } } }
Goede praktijk:
index.cs
123456789101112131415161718192021222324252627282930class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; this.balance = balance; monthlyInterest(); } // This method might be used in other places too private void monthlyInterest() { if(accountType == "Savings") { // Plus 1 Percent balance += balance * 0.01; } else if(accountType == "HighYieldSavings") { // Plus 5 percent balance += balance * 0.05; } } }
Belangrijke attributen initialiseren: het is noodzakelijk om alle belangrijke attributen van een object te initialiseren met de juiste waarden om correcte werking te garanderen - zelfs als het een constructor zonder argumenten betreft.
Slechte praktijk:
index.cs
123456789101112131415public class Car { private string brand; private string model; private int year; private double price; // Constructor does not initialize important attributes // It is also generally not a good idea to have constructors without any arguments if they're not needed. public Car() { // No initialization of attributes Console.WriteLine("Car Created"); } }
Goede praktijk:
index.cs
123456789101112131415161718192021222324252627282930313233343536373839public class Car { private string brand; private string model; private int year; private double price; // Good: Constructor initializes important attributes // It also checks if the values are correct // In this case the if-else statements are not unnecessary since they are important for ensuring that the object functions correctly. public Car(string brand, string model, int year, double price) { this.brand = brand; this.model = model; // Validate and set the year // The first public car was created in 1886 :) if (year > 1886) { this.year = year; } else { Console.WriteLine("Invalid year. Setting year to default."); this.year = DateTime.Now.Year; // Set to current year as default } // Validate and set the price if (price >= 0) { this.price = price; } else { Console.WriteLine("Invalid price. Setting price to default."); this.price = 0; // Set to a default value } } }
1. Welke eigenschap stelt ons in staat om meerdere constructors voor een klasse te maken?
2. Het kan nodig zijn om een van de concepten uit de vorige secties te gebruiken in deze quiz. De onderstaande code bevat een fout op regel 15 en 16. Bekijk de code grondig en bepaal wat een efficiënte oplossing is voor deze fout:
Bedankt voor je feedback!
Vraag AI
Vraag AI
Vraag wat u wilt of probeer een van de voorgestelde vragen om onze chat te starten.
Can you explain more about constructor overloading?
What happens if I don't call the base constructor in a derived class?
Can you give an example of multilevel inheritance with constructors?
Awesome!
Completion rate improved to 2.04
Overerving
Veeg om het menu te tonen
In de vorige sectie heb je gekeken naar het concept van afgeleide klassen. Deze eigenschap van een klasse om eigenschappen van een andere klasse over te nemen, wordt overerving genoemd.
Hoewel je het concept van overerving al kent, wordt het deze keer op een iets uitgebreidere manier behandeld om het grondiger te begrijpen.
Ter herziening volgt hier een voorbeeld van overerving:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kilogram (1 kg = 2.2 pounds) } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Cat myCat = new Cat(); Dog myDog = new Dog(); myCat.speak(); myDog.speak(); } }
De directive #pragma warning disable "warning code"
kan worden gebruikt om bepaalde compilerwaarschuwingen uit te schakelen. Het is over het algemeen af te raden om waarschuwingen uit te schakelen, omdat het negeren ervan tot onverwacht gedrag in het programma kan leiden.
De bovenstaande code bevat één bovenliggende klasse genaamd Mammal
en twee afgeleide klassen genaamd Cat
en Dog
.
Let op dat geen van de klassen expliciet een constructor heeft gedefinieerd, wat betekent dat deze klassen een standaardconstructor zullen gebruiken wanneer een object wordt aangemaakt.
Een standaardconstructor wordt automatisch door de programmeertaal geleverd als een klasse geen expliciet gedefinieerde constructors heeft. Een standaardconstructor is in feite een lege constructor die geen code bevat, bijvoorbeeld public className() {}
is hoe een standaardconstructor eruit kan zien. Omdat deze geen attributen expliciet initialiseert, krijgen alle attributen standaardwaarden - ook wel nulwaarden genoemd.
Laten we handmatig een constructor maken voor de klasse Mammal
, die een Mammal
-object initialiseert met bepaalde gegevens:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
Als u probeert dit programma te compileren, zullen er enkele fouten in de console verschijnen. Om deze fouten te begrijpen, moet u eerst twee belangrijke concepten met betrekking tot constructors begrijpen.
Het eerste concept is dat zodra u expliciet een constructor voor een klasse definieert, die klasse geen standaardconstructor meer heeft. De expliciet gedefinieerde constructor wordt dan de hoofdconstructor van die klasse, wat in dit geval het volgende betekent:
index.cs
12345public Mammal(int age, float weight) { this.age = age; this.weight = weight; }
Daarom moeten bij het aanmaken van een nieuw object altijd de vereiste argumenten van de constructor in de juiste volgorde worden doorgegeven:
index.cs
1234567// Incorrect ways to create 'Mammal', will show an error Mammal m1 = new Mammal(); Mammal m1 = new Mammal(10); Mammal m1 = new Mammal(42.0f); // Correct way to create 'Mammal', will execute fine. Mammal m1 = new Mammal(10, 42.0f);
Ten tweede kunnen afgeleide klassen ook constructors hebben; echter, voordat de constructor van een afgeleide klasse wordt aangeroepen, wordt eerst de constructor van de basisklasse (ouder) aangeroepen:
index.cs
1234567891011121314151617181920212223242526272829303132333435#pragma warning disable CS0169 // To disable some unnecessary warnings, using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg // No attribute is initialized explicitly in this constructor // Hence, all attributes will take up "zero" values // It is similar to a "default" constructor except it outputs a message public Mammal() { Console.WriteLine("Mammal Constructor Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog Constructor Called"); } } class ConsoleApp { static void Main() { Dog myDog = new Dog(); } }
Wanneer je deze code uitvoert, zie je dat de WriteLine()
-methode uit de constructor van 'Mammal', de bovenliggende klasse, automatisch wordt aangeroepen. Dit betekent dat het een regel is dat de constructor van de basisklasse (ook wel de base constructor genoemd) altijd wordt aangeroepen vóór de constructor van de afgeleide klasse.
Deze regel geldt ook bij meerlagige overerving:

In het bovenstaande diagram roept de Kitten
-constructor eerst de Cat
-constructor aan, maar omdat Cat
ook een afgeleide klasse is, roept deze eerst de Mammal
-constructor aan, en Mammal
roept op zijn beurt de Animal
-constructor aan voordat zijn eigen constructor wordt uitgevoerd. Hierdoor is de eerste constructor die wordt uitgevoerd die van de superklasse — namelijk de constructor van de Animal
-klasse, waarna het verder naar beneden gaat.
Als de constructor van de bovenliggende klasse geen argumenten vereist, wordt deze automatisch door de compiler aangeroepen. Dit is de reden waarom de 'Mammal'-constructor in het bovenstaande voorbeeld automatisch werd aangeroepen. Laten we echter opnieuw naar de foutieve code kijken:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
In de bovenstaande code treden er twee fouten op die in feite betekenen dat de base constructors niet handmatig zijn aangeroepen — omdat deze enkele argumenten vereisen, moet je deze handmatig aanroepen. De basis-syntaxis voor het handmatig aanroepen van de constructor van de bovenliggende klasse is als volgt:
index.cs
12345678910class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }
Voorbeeld:
index.cs
1234567891011121314151617181920212223242526272829303132using System; class ExampleParentClass { int value1; int value2; public ExampleParentClass(int value1, int value2) { this.value1 = value1; } } class ExampleDerivedClass : ExampleParentClass { int value3; // The value1 and value2 arguments are passed to the base class's contructor public ExampleDerivedClass(int value1, int value2, int value3) : base (value1, value2) { this.value3 = value3; } } class ConsoleApp { static void Main() { var testObject = new ExampleDerivedClass(5, 7, 9); } }
Met deze syntaxis kun je alle vereiste gegevens doorgeven aan de Mammal
constructor via de Cat
en Dog
constructors om de fout die je eerder kreeg op te lossen:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public Cat(int age, float weight, string furPattern) : base(age, weight) { this.furPattern = furPattern; } public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); // Creating a "Dog" object with some data Dog d1 = new Dog(10, 42.5f, "Dobermann"); Console.WriteLine("Executed Successfully"); } }
Een andere belangrijke eigenschap van constructors is dat je constructors kunt overbelasten, net zoals we elke andere methode kunnen overbelasten. Je kunt meerdere constructors maken met een verschillend aantal argumenten:
index.cs
12345678910111213141516171819202122232425class Mammal { int age; float weight; // kg // 1st constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }
In dit geval heeft de klasse Mammal
3 constructors. Hierdoor kun je een mammal-object op 3 verschillende manieren initialiseren of aanmaken, en de compiler kiest welke constructor wordt aangeroepen op basis van het aantal en type argumenten:
index.cs
1234// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);
Dit betekent ook dat je een van de drie constructors kunt aanroepen vanuit de constructors van de afgeleide klasse. Bijvoorbeeld, al deze zijn geldig:
index.cs
123456789101112131415161718// Using 3rd base constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } // Using 2nd base constructor public Dog(int age, string breed) : base(age) { this.breed = breed; } // Using 1st base constructor // If the base constructor has no arguments then it is automatically called (similar to the default constructor), so we don't necessarily need to write 'base()' public Dog(string breed) { this.breed = breed; }
Laten we de bovenstaande twee codefragmenten samenvoegen en enkele Console.WriteLine
-instructies toevoegen om te zien in welke volgorde de constructors worden uitgevoerd, zodat we het resultaat praktisch kunnen waarnemen:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394using System; class Mammal { int age; float weight; // kg // 1st Constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor // The attributes are initialized with zero values Console.WriteLine("Mammal - Constructor 1 Called"); } // 2nd Constructor public Mammal(int age) { this.age = age; Console.WriteLine("Mammal - Constructor 2 Called"); } // 3rd Constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; Console.WriteLine("Mammal - Constructor 3 Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog - Constructor 1 Called"); } // Using 1st Mammal constructor // We don't necessarily need to write 'base()' in this case // It automatically finds and calls the constructor with no arguments public Dog(string breed) { this.breed = breed; Console.WriteLine("Dog - Constructor 2 Called"); } // Using 2nd Mammal constructor public Dog(int age, string breed) : base(age) { this.breed = breed; Console.WriteLine("Dog - Constructor 3 Called"); } // Using 3rd Mammal constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; Console.WriteLine("Dog - Constructor 4 Called"); } public void speak() { Console.WriteLine("Woof!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object using different constructors Mammal m1 = new Mammal(10, 42.0f); Mammal m2 = new Mammal(10); Mammal m3 = new Mammal(); Console.WriteLine("----------"); // Seperator, for ease of reading output // Creating a "Dog" object using different constructors Dog d1 = new Dog(10, 42.0f, "Dobermann"); Console.WriteLine(""); Dog d2 = new Dog(10, "Dobermann"); Console.WriteLine(""); Dog d3 = new Dog("Dobermann"); Console.WriteLine(""); Dog d4 = new Dog(); } }
Nu je bekend bent met verschillende kenmerken van overerving, is het ook belangrijk te weten hoe en wanneer je deze correct toepast. Houd rekening met de volgende aandachtspunten bij het overwegen van een op overerving gebaseerde klassenstructuur:
Balans tussen eenvoud en flexibiliteit: Constructor-overloading maakt het mogelijk om meerdere verschillende constructors te hebben die verschillende typen argumenten accepteren, maar overmatig gebruik kan de code complex en moeilijk te onderhouden maken. Het is een best practice om de klassecode kort, bondig en overzichtelijk te houden. Vermijd het maken van te veel constructors voor een klasse om een balans tussen eenvoud en flexibiliteit te behouden.
Houd constructors eenvoudig: Constructors dienen voornamelijk verantwoordelijk te zijn voor het initialiseren van een object met basisgegevens. Het is een best practice om onnodige verwerking en complexe logica binnen een constructor te vermijden. Als er berekeningen of logica nodig zijn, is het beter hiervoor een aparte methode te maken.
;Slechte praktijk:;
index.cs
123456789101112131415161718192021222324252627class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; if (accountType == "Savings") { // Plus 1 Percent this.balance = balance + balance * 0.01; } else if (accountType == "HighYieldSavings") { // Plus 5 percent this.balance = balance + balance * 0.05; } else { this.balance = balance; } } }
Goede praktijk:
index.cs
123456789101112131415161718192021222324252627282930class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; this.balance = balance; monthlyInterest(); } // This method might be used in other places too private void monthlyInterest() { if(accountType == "Savings") { // Plus 1 Percent balance += balance * 0.01; } else if(accountType == "HighYieldSavings") { // Plus 5 percent balance += balance * 0.05; } } }
Belangrijke attributen initialiseren: het is noodzakelijk om alle belangrijke attributen van een object te initialiseren met de juiste waarden om correcte werking te garanderen - zelfs als het een constructor zonder argumenten betreft.
Slechte praktijk:
index.cs
123456789101112131415public class Car { private string brand; private string model; private int year; private double price; // Constructor does not initialize important attributes // It is also generally not a good idea to have constructors without any arguments if they're not needed. public Car() { // No initialization of attributes Console.WriteLine("Car Created"); } }
Goede praktijk:
index.cs
123456789101112131415161718192021222324252627282930313233343536373839public class Car { private string brand; private string model; private int year; private double price; // Good: Constructor initializes important attributes // It also checks if the values are correct // In this case the if-else statements are not unnecessary since they are important for ensuring that the object functions correctly. public Car(string brand, string model, int year, double price) { this.brand = brand; this.model = model; // Validate and set the year // The first public car was created in 1886 :) if (year > 1886) { this.year = year; } else { Console.WriteLine("Invalid year. Setting year to default."); this.year = DateTime.Now.Year; // Set to current year as default } // Validate and set the price if (price >= 0) { this.price = price; } else { Console.WriteLine("Invalid price. Setting price to default."); this.price = 0; // Set to a default value } } }
1. Welke eigenschap stelt ons in staat om meerdere constructors voor een klasse te maken?
2. Het kan nodig zijn om een van de concepten uit de vorige secties te gebruiken in deze quiz. De onderstaande code bevat een fout op regel 15 en 16. Bekijk de code grondig en bepaal wat een efficiënte oplossing is voor deze fout:
Bedankt voor je feedback!