Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lære Arv | OOP-Prinsipper
C# Utover Det Grunnleggende

bookArv

Du har sett på konseptet med avledede klasser i forrige seksjon. Denne egenskapen til en klasse, hvor den kan arve egenskaper fra en annen klasse, kalles arv.

Selv om du allerede kjenner til konseptet arv, vil du denne gangen gå gjennom det på en litt mer omfattende måte for å få en grundigere forståelse.

Som en rask repetisjon følger her et eksempel på arv:

index.cs

index.cs

copy
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(); } }
Note
Merk

Direktivet #pragma warning disable "warning code" kan brukes for å deaktivere visse kompilatoradvarsler. Det frarådes generelt å deaktivere advarsler, da det å ignorere dem kan føre til uventet oppførsel i programmet.

Koden ovenfor inneholder én foreldreklasse kalt Mammal og to avledede klasser kalt Cat og Dog.

Merk at ingen av klassene har noen eksplisitt definert konstruktør, noe som betyr at disse klassene vil bruke en standardkonstruktør når et objekt opprettes.

Note
Merk

En standardkonstruktør blir automatisk levert av programmeringsspråket dersom en klasse ikke har noen eksplisitt definerte konstruktører. En standardkonstruktør er i bunn og grunn en tom konstruktør som ikke inneholder noen kode, for eksempel public className() {} er hvordan en standardkonstruktør kan se ut. Siden den ikke initialiserer noen attributter eksplisitt, får alle attributtene standardverdier – også kalt nullverdier.

La oss lage en konstruktør for Mammal-klassen manuelt, som initialiserer et Mammal-objekt med noen data:

index.cs

index.cs

copy
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); } }

Hvis du prøver å kompilere dette programmet, vil det vises noen feil i konsollen. For å forstå disse feilene må du først forstå to viktige konsepter knyttet til konstruktører.

Det første er at når du eksplisitt definerer en konstruktør for en klasse, har ikke klassen lenger en standardkonstruktør, og dermed blir den eksplisitt definerte konstruktøren hovedkonstruktøren for klassen, som i dette tilfellet er:

index.cs

index.cs

copy
12345
public Mammal(int age, float weight) { this.age = age; this.weight = weight; }

Derfor må nødvendige argumenter til konstruktøren alltid oppgis i riktig rekkefølge når et nytt objekt opprettes:

index.cs

index.cs

copy
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);

For det andre kan avledede klasser også ha konstruktører, men før konstruktøren til en avledet klasse kalles, vil konstruktøren til basisklassen (forelderen) også bli kalt:

index.cs

index.cs

copy
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(); } }

Når du kjører denne koden ser du at WriteLine()-metoden fra 'Mammal'-konstruktøren, som er foreldreklassen, automatisk blir kalt. Dette betyr at det er en regel at basisklassens konstruktør (også kalt basekonstruktør) alltid blir kalt før den avledede klassens konstruktør.

Denne regelen gjelder også ved flernivåsarv:

I diagrammet ovenfor kaller Kitten-konstruktøren Cat-konstruktøren før sin egen, men siden Cat også er en avledet klasse, kaller den Mammal-konstruktøren før seg selv, og Mammal kaller Animal-konstruktøren før sin konstruktør. Derfor er den første konstruktøren som kjøres, konstruktøren til superklassen — altså konstruktøren til Animal-klassen, og deretter går det videre nedover.

Hvis konstruktøren til foreldrerklassen ikke tar noen argumenter, blir den automatisk kalt av kompilatoren. Dette er grunnen til at 'Mammal'-konstruktøren i eksemplet ovenfor ble kalt automatisk. La oss imidlertid se på den feilaktige koden igjen:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243
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); } }

I koden ovenfor får du to feil som i hovedsak betyr at du ikke har kalt basekonstruktørene manuelt — siden den krever noen argumenter, må du kalle den manuelt. Den grunnleggende syntaksen for å kalle konstruktøren til foreldrerklassen manuelt er følgende:

index.cs

index.cs

copy
12345678910
class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }

Eksempel:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132
using 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); } }

Ved å bruke denne syntaksen kan du sende all nødvendig data til Mammal-konstruktøren gjennom Cat- og Dog-konstruktørene for å rette opp feilen du fikk tidligere:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
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 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"); } }

En annen viktig egenskap ved konstruktører er at du kan overbelaste konstruktører på samme måte som vi overbelaster andre metoder. Du kan lage flere konstruktører med varierende antall argumenter:

index.cs

index.cs

copy
12345678910111213141516171819202122232425
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 } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }

I dette tilfellet har klassen Mammal 3 konstruktører. Du kan derfor initialisere eller opprette et mammal-objekt på 3 forskjellige måter, og kompilatoren vil velge hvilken konstruktør som skal kalles basert på antall og type argumenter:

index.cs

index.cs

copy
1234
// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);

Dette betyr også at du kan kalle hvilken som helst av de tre konstruktørene fra konstruktørene i den avledede klassen. For eksempel er alle disse gyldige:

index.cs

index.cs

copy
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; }

La oss sette sammen de to kodestykkene ovenfor og legge til noen Console.WriteLine-setninger for å se i hvilken rekkefølge konstruktørene kjøres, slik at vi praktisk kan se resultatene:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
using 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(); } }

Nå som du kjenner til ulike egenskaper ved arv, bør du også vite hvordan og når de skal brukes riktig. Følgende punkter bør vurderes når du planlegger en klasse­struktur basert på arv:

Balanse mellom enkelhet og fleksibilitet: Konstruktøroverlasting gir mulighet til å ha flere ulike konstruktører med forskjellige argumenttyper, men overdreven bruk kan gjøre koden mer komplisert og vanskelig å vedlikeholde. Det er god praksis å holde klassekoden kort, presis og oversiktlig. Unngå å lage for mange konstruktører for en klasse for å opprettholde balansen mellom enkelhet og fleksibilitet.

Hold konstruktører enkle: Konstruktører bør hovedsakelig brukes til å initialisere et objekt med grunnleggende data. Det er god praksis å unngå unødvendig prosessering og kompleks logikk i en konstruktør. Dersom det er behov for beregninger eller logikk, bør dette legges i en egen metode.

;Dårlig praksis:;

index.cs

index.cs

copy
123456789101112131415161718192021222324252627
class 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; } } }

God praksis:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930
class 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; } } }

Initialiser viktige attributter: det er nødvendig å initialisere alle viktige attributter i et objekt med korrekte verdier for å sikre at de fungerer riktig – selv om det er en konstruktør uten argumenter.

Dårlig praksis:

index.cs

index.cs

copy
123456789101112131415
public 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"); } }

God praksis:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930313233343536373839
public 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. Hvilken egenskap gjør det mulig å opprette flere konstruktører for en klasse?

2. Du kan ha behov for å bruke et av konseptene fra de forrige seksjonene i denne quizen. Koden under har en feil på linje 15 og 16. Se nøye på koden og avgjør hva som er en effektiv løsning på denne feilen:

question mark

Hvilken egenskap gjør det mulig å opprette flere konstruktører for en klasse?

Select the correct answer

question mark

Du kan ha behov for å bruke et av konseptene fra de forrige seksjonene i denne quizen. Koden under har en feil på linje 15 og 16. Se nøye på koden og avgjør hva som er en effektiv løsning på denne feilen:

Select the correct answer

Alt var klart?

Hvordan kan vi forbedre det?

Takk for tilbakemeldingene dine!

Seksjon 5. Kapittel 2

Spør AI

expand

Spør AI

ChatGPT

Spør om hva du vil, eller prøv ett av de foreslåtte spørsmålene for å starte chatten vår

Awesome!

Completion rate improved to 2.04

bookArv

Sveip for å vise menyen

Du har sett på konseptet med avledede klasser i forrige seksjon. Denne egenskapen til en klasse, hvor den kan arve egenskaper fra en annen klasse, kalles arv.

Selv om du allerede kjenner til konseptet arv, vil du denne gangen gå gjennom det på en litt mer omfattende måte for å få en grundigere forståelse.

Som en rask repetisjon følger her et eksempel på arv:

index.cs

index.cs

copy
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(); } }
Note
Merk

Direktivet #pragma warning disable "warning code" kan brukes for å deaktivere visse kompilatoradvarsler. Det frarådes generelt å deaktivere advarsler, da det å ignorere dem kan føre til uventet oppførsel i programmet.

Koden ovenfor inneholder én foreldreklasse kalt Mammal og to avledede klasser kalt Cat og Dog.

Merk at ingen av klassene har noen eksplisitt definert konstruktør, noe som betyr at disse klassene vil bruke en standardkonstruktør når et objekt opprettes.

Note
Merk

En standardkonstruktør blir automatisk levert av programmeringsspråket dersom en klasse ikke har noen eksplisitt definerte konstruktører. En standardkonstruktør er i bunn og grunn en tom konstruktør som ikke inneholder noen kode, for eksempel public className() {} er hvordan en standardkonstruktør kan se ut. Siden den ikke initialiserer noen attributter eksplisitt, får alle attributtene standardverdier – også kalt nullverdier.

La oss lage en konstruktør for Mammal-klassen manuelt, som initialiserer et Mammal-objekt med noen data:

index.cs

index.cs

copy
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); } }

Hvis du prøver å kompilere dette programmet, vil det vises noen feil i konsollen. For å forstå disse feilene må du først forstå to viktige konsepter knyttet til konstruktører.

Det første er at når du eksplisitt definerer en konstruktør for en klasse, har ikke klassen lenger en standardkonstruktør, og dermed blir den eksplisitt definerte konstruktøren hovedkonstruktøren for klassen, som i dette tilfellet er:

index.cs

index.cs

copy
12345
public Mammal(int age, float weight) { this.age = age; this.weight = weight; }

Derfor må nødvendige argumenter til konstruktøren alltid oppgis i riktig rekkefølge når et nytt objekt opprettes:

index.cs

index.cs

copy
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);

For det andre kan avledede klasser også ha konstruktører, men før konstruktøren til en avledet klasse kalles, vil konstruktøren til basisklassen (forelderen) også bli kalt:

index.cs

index.cs

copy
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(); } }

Når du kjører denne koden ser du at WriteLine()-metoden fra 'Mammal'-konstruktøren, som er foreldreklassen, automatisk blir kalt. Dette betyr at det er en regel at basisklassens konstruktør (også kalt basekonstruktør) alltid blir kalt før den avledede klassens konstruktør.

Denne regelen gjelder også ved flernivåsarv:

I diagrammet ovenfor kaller Kitten-konstruktøren Cat-konstruktøren før sin egen, men siden Cat også er en avledet klasse, kaller den Mammal-konstruktøren før seg selv, og Mammal kaller Animal-konstruktøren før sin konstruktør. Derfor er den første konstruktøren som kjøres, konstruktøren til superklassen — altså konstruktøren til Animal-klassen, og deretter går det videre nedover.

Hvis konstruktøren til foreldrerklassen ikke tar noen argumenter, blir den automatisk kalt av kompilatoren. Dette er grunnen til at 'Mammal'-konstruktøren i eksemplet ovenfor ble kalt automatisk. La oss imidlertid se på den feilaktige koden igjen:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243
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); } }

I koden ovenfor får du to feil som i hovedsak betyr at du ikke har kalt basekonstruktørene manuelt — siden den krever noen argumenter, må du kalle den manuelt. Den grunnleggende syntaksen for å kalle konstruktøren til foreldrerklassen manuelt er følgende:

index.cs

index.cs

copy
12345678910
class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }

Eksempel:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132
using 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); } }

Ved å bruke denne syntaksen kan du sende all nødvendig data til Mammal-konstruktøren gjennom Cat- og Dog-konstruktørene for å rette opp feilen du fikk tidligere:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
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 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"); } }

En annen viktig egenskap ved konstruktører er at du kan overbelaste konstruktører på samme måte som vi overbelaster andre metoder. Du kan lage flere konstruktører med varierende antall argumenter:

index.cs

index.cs

copy
12345678910111213141516171819202122232425
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 } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }

I dette tilfellet har klassen Mammal 3 konstruktører. Du kan derfor initialisere eller opprette et mammal-objekt på 3 forskjellige måter, og kompilatoren vil velge hvilken konstruktør som skal kalles basert på antall og type argumenter:

index.cs

index.cs

copy
1234
// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);

Dette betyr også at du kan kalle hvilken som helst av de tre konstruktørene fra konstruktørene i den avledede klassen. For eksempel er alle disse gyldige:

index.cs

index.cs

copy
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; }

La oss sette sammen de to kodestykkene ovenfor og legge til noen Console.WriteLine-setninger for å se i hvilken rekkefølge konstruktørene kjøres, slik at vi praktisk kan se resultatene:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
using 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(); } }

Nå som du kjenner til ulike egenskaper ved arv, bør du også vite hvordan og når de skal brukes riktig. Følgende punkter bør vurderes når du planlegger en klasse­struktur basert på arv:

Balanse mellom enkelhet og fleksibilitet: Konstruktøroverlasting gir mulighet til å ha flere ulike konstruktører med forskjellige argumenttyper, men overdreven bruk kan gjøre koden mer komplisert og vanskelig å vedlikeholde. Det er god praksis å holde klassekoden kort, presis og oversiktlig. Unngå å lage for mange konstruktører for en klasse for å opprettholde balansen mellom enkelhet og fleksibilitet.

Hold konstruktører enkle: Konstruktører bør hovedsakelig brukes til å initialisere et objekt med grunnleggende data. Det er god praksis å unngå unødvendig prosessering og kompleks logikk i en konstruktør. Dersom det er behov for beregninger eller logikk, bør dette legges i en egen metode.

;Dårlig praksis:;

index.cs

index.cs

copy
123456789101112131415161718192021222324252627
class 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; } } }

God praksis:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930
class 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; } } }

Initialiser viktige attributter: det er nødvendig å initialisere alle viktige attributter i et objekt med korrekte verdier for å sikre at de fungerer riktig – selv om det er en konstruktør uten argumenter.

Dårlig praksis:

index.cs

index.cs

copy
123456789101112131415
public 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"); } }

God praksis:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930313233343536373839
public 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. Hvilken egenskap gjør det mulig å opprette flere konstruktører for en klasse?

2. Du kan ha behov for å bruke et av konseptene fra de forrige seksjonene i denne quizen. Koden under har en feil på linje 15 og 16. Se nøye på koden og avgjør hva som er en effektiv løsning på denne feilen:

question mark

Hvilken egenskap gjør det mulig å opprette flere konstruktører for en klasse?

Select the correct answer

question mark

Du kan ha behov for å bruke et av konseptene fra de forrige seksjonene i denne quizen. Koden under har en feil på linje 15 og 16. Se nøye på koden og avgjør hva som er en effektiv løsning på denne feilen:

Select the correct answer

Alt var klart?

Hvordan kan vi forbedre det?

Takk for tilbakemeldingene dine!

Seksjon 5. Kapittel 2
some-alt