Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lære Arv | OOP-Principper
C# Ud Over Det Grundlæggende

bookArv

Du har tidligere set på konceptet afledte klasser i det forrige afsnit. Denne egenskab ved en klasse til at arve egenskaber fra en anden klasse kaldes arv.

Selvom du allerede kender til begrebet arv, vil du denne gang gennemgå det på en lidt mere omfattende måde for at opnå en dybere forståelse.

Som en hurtig repetition ses 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
Bemærk

Direktivet #pragma warning disable "warning code" kan bruges til at deaktivere bestemte compiler-advarsler. Det frarådes generelt at deaktivere advarsler, da det at ignorere dem kan føre til uventet adfærd i programmet.

Koden ovenfor indeholder én overordnet klasse kaldet Mammal og to afledte klasser kaldet Cat og Dog.

Bemærk, at ingen af klasserne har nogen konstruktør eksplicit defineret, hvilket betyder, at disse klasser vil anvende en standardkonstruktør, når et objekt oprettes.

Note
Bemærk

En standardkonstruktør leveres automatisk af programmeringssproget, hvis en klasse ikke har nogen eksplicit definerede konstruktører. En standardkonstruktør er grundlæggende en tom konstruktør, som ikke indeholder nogen kode, for eksempel public className() {} er, hvordan en standardkonstruktør kan se ud. Da den ikke initialiserer nogen attributter eksplicit, får alle attributter standardværdier – også kaldet nulværdier.

Lad os manuelt oprette en konstruktør til Mammal-klassen, som initialiserer et Mammal-objekt med nogle 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 forsøger at kompilere dette program, vil der opstå nogle fejl i konsollen. For at forstå disse fejl skal du først kende til to vigtige begreber relateret til konstruktører.

Det første er, at når du eksplicit definerer en konstruktør for en klasse, har denne klasse ikke længere en standardkonstruktør, og derfor bliver den eksplicit definerede konstruktør hovedkonstruktøren for klassen, hvilket i dette tilfælde er:

index.cs

index.cs

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

Derfor skal du altid angive de nødvendige argumenter til konstruktøren i den korrekte rækkefølge, når du opretter et nyt objekt:

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 andet kan afledte klasser også have konstruktører, men før konstruktøren for en afledt klasse kaldes, kaldes konstruktøren for basisklassen (forælderen) også:

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 kører denne kode, vil du se, at WriteLine()-metoden fra 'Mammal'-konstruktøren, som er forældreklassen, automatisk bliver kaldt. Dette betyder, at det er en regel, at basisklassens konstruktør (også kaldet basekonstruktøren) altid bliver kaldt før den afledte klasses konstruktør.

Denne regel gælder også ved flertrinsarv:

I ovenstående diagram kalder Kitten-konstruktøren Cat-konstruktøren før sin egen, men da Cat også er en afledt klasse, kalder den Mammal-konstruktøren før sig selv, og Mammal kalder Animal-konstruktøren før sin egen konstruktør. Derfor er den første konstruktør, der udføres, konstruktøren for superklassen — altså Animal-klassens konstruktør, hvorefter det fortsætter derfra.

Hvis forældreklassens konstruktør ikke tager nogen argumenter, bliver den automatisk kaldt af compileren. Dette er grunden til, at 'Mammal'-konstruktøren i ovenstående eksempel blev kaldt automatisk. Lad os dog se på den fejlbehæftede kode igen:

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 ovenstående kode opstår der to fejl, som grundlæggende betyder, at du ikke manuelt har kaldt base-konstruktørerne — da de kræver nogle argumenter, skal du manuelt kalde dem. Den grundlæggende syntaks for manuelt at kalde forældreklassens konstruktør 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 at bruge denne syntaks kan du videregive alle nødvendige data til Mammal-konstruktøren gennem Cat- og Dog-konstruktørerne for at rette den fejl, du tidligere fik:

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 anden vigtig egenskab ved konstruktører er, at du kan overloade konstruktører på samme måde, som vi overloader andre metoder. Du kan oprette flere konstruktører med varierende antal 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 tilfælde har Mammal-klassen 3 konstruktører. Du kan derfor initialisere eller oprette et mammal-objekt på 3 forskellige måder, og compileren vælger, hvilken konstruktør der skal kaldes, baseret på antallet og typen af 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 betyder også, at du kan kalde en af de 3 konstruktører fra den afledte klasses konstruktører. 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; }

Lad os samle de to ovenstående kodeeksempler og tilføje nogle Console.WriteLine-udtryk for at se, i hvilken rækkefølge konstruktørerne udføres, så vi praktisk kan se resultaterne:

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

Nu hvor du kender til forskellige egenskaber ved arv, bør du også vide, hvordan og hvornår de bruges korrekt. Følgende er nogle ting, du skal huske på, når du overvejer en klasse-struktur baseret på arv:

Balance mellem enkelhed og fleksibilitet: Konstruktør-overbelastning giver mulighed for at have mange forskellige konstruktører, der tager forskellige typer argumenter, men overdreven brug kan gøre koden mere kompliceret og svær at vedligeholde. Det er god praksis at holde klassekoden kort, præcis og bekvem. Undgå at lave for mange konstruktører for en klasse for at opretholde en balance mellem enkelhed og fleksibilitet.

Hold konstruktører enkle: Konstruktører bør primært være ansvarlige for at initialisere et objekt med grundlæggende data. Det er god praksis at undgå unødvendig behandling og kompleks logik i en konstruktør. Hvis der er behov for beregning eller logik, er det bedre at oprette en separat metode til det.

;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 vigtige attributter: det er nødvendigt at initialisere alle vigtige attributter for et objekt med korrekte værdier for at sikre, at de fungerer korrekt – selv hvis det er en konstruktør uden 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 funktion gør det muligt at oprette flere konstruktører for en klasse?

2. Du kan få brug for at anvende et af koncepterne fra de foregående afsnit i denne quiz. Koden nedenfor indeholder en fejl på linje 15 og 16. Undersøg koden grundigt og afgør, hvad der er en effektiv løsning på denne fejl:

question mark

Hvilken funktion gør det muligt at oprette flere konstruktører for en klasse?

Select the correct answer

question mark

Du kan få brug for at anvende et af koncepterne fra de foregående afsnit i denne quiz. Koden nedenfor indeholder en fejl på linje 15 og 16. Undersøg koden grundigt og afgør, hvad der er en effektiv løsning på denne fejl:

Select the correct answer

Var alt klart?

Hvordan kan vi forbedre det?

Tak for dine kommentarer!

Sektion 5. Kapitel 2

Spørg AI

expand

Spørg AI

ChatGPT

Spørg om hvad som helst eller prøv et af de foreslåede spørgsmål for at starte vores chat

Awesome!

Completion rate improved to 2.04

bookArv

Stryg for at vise menuen

Du har tidligere set på konceptet afledte klasser i det forrige afsnit. Denne egenskab ved en klasse til at arve egenskaber fra en anden klasse kaldes arv.

Selvom du allerede kender til begrebet arv, vil du denne gang gennemgå det på en lidt mere omfattende måde for at opnå en dybere forståelse.

Som en hurtig repetition ses 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
Bemærk

Direktivet #pragma warning disable "warning code" kan bruges til at deaktivere bestemte compiler-advarsler. Det frarådes generelt at deaktivere advarsler, da det at ignorere dem kan føre til uventet adfærd i programmet.

Koden ovenfor indeholder én overordnet klasse kaldet Mammal og to afledte klasser kaldet Cat og Dog.

Bemærk, at ingen af klasserne har nogen konstruktør eksplicit defineret, hvilket betyder, at disse klasser vil anvende en standardkonstruktør, når et objekt oprettes.

Note
Bemærk

En standardkonstruktør leveres automatisk af programmeringssproget, hvis en klasse ikke har nogen eksplicit definerede konstruktører. En standardkonstruktør er grundlæggende en tom konstruktør, som ikke indeholder nogen kode, for eksempel public className() {} er, hvordan en standardkonstruktør kan se ud. Da den ikke initialiserer nogen attributter eksplicit, får alle attributter standardværdier – også kaldet nulværdier.

Lad os manuelt oprette en konstruktør til Mammal-klassen, som initialiserer et Mammal-objekt med nogle 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 forsøger at kompilere dette program, vil der opstå nogle fejl i konsollen. For at forstå disse fejl skal du først kende til to vigtige begreber relateret til konstruktører.

Det første er, at når du eksplicit definerer en konstruktør for en klasse, har denne klasse ikke længere en standardkonstruktør, og derfor bliver den eksplicit definerede konstruktør hovedkonstruktøren for klassen, hvilket i dette tilfælde er:

index.cs

index.cs

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

Derfor skal du altid angive de nødvendige argumenter til konstruktøren i den korrekte rækkefølge, når du opretter et nyt objekt:

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 andet kan afledte klasser også have konstruktører, men før konstruktøren for en afledt klasse kaldes, kaldes konstruktøren for basisklassen (forælderen) også:

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 kører denne kode, vil du se, at WriteLine()-metoden fra 'Mammal'-konstruktøren, som er forældreklassen, automatisk bliver kaldt. Dette betyder, at det er en regel, at basisklassens konstruktør (også kaldet basekonstruktøren) altid bliver kaldt før den afledte klasses konstruktør.

Denne regel gælder også ved flertrinsarv:

I ovenstående diagram kalder Kitten-konstruktøren Cat-konstruktøren før sin egen, men da Cat også er en afledt klasse, kalder den Mammal-konstruktøren før sig selv, og Mammal kalder Animal-konstruktøren før sin egen konstruktør. Derfor er den første konstruktør, der udføres, konstruktøren for superklassen — altså Animal-klassens konstruktør, hvorefter det fortsætter derfra.

Hvis forældreklassens konstruktør ikke tager nogen argumenter, bliver den automatisk kaldt af compileren. Dette er grunden til, at 'Mammal'-konstruktøren i ovenstående eksempel blev kaldt automatisk. Lad os dog se på den fejlbehæftede kode igen:

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 ovenstående kode opstår der to fejl, som grundlæggende betyder, at du ikke manuelt har kaldt base-konstruktørerne — da de kræver nogle argumenter, skal du manuelt kalde dem. Den grundlæggende syntaks for manuelt at kalde forældreklassens konstruktør 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 at bruge denne syntaks kan du videregive alle nødvendige data til Mammal-konstruktøren gennem Cat- og Dog-konstruktørerne for at rette den fejl, du tidligere fik:

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 anden vigtig egenskab ved konstruktører er, at du kan overloade konstruktører på samme måde, som vi overloader andre metoder. Du kan oprette flere konstruktører med varierende antal 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 tilfælde har Mammal-klassen 3 konstruktører. Du kan derfor initialisere eller oprette et mammal-objekt på 3 forskellige måder, og compileren vælger, hvilken konstruktør der skal kaldes, baseret på antallet og typen af 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 betyder også, at du kan kalde en af de 3 konstruktører fra den afledte klasses konstruktører. 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; }

Lad os samle de to ovenstående kodeeksempler og tilføje nogle Console.WriteLine-udtryk for at se, i hvilken rækkefølge konstruktørerne udføres, så vi praktisk kan se resultaterne:

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

Nu hvor du kender til forskellige egenskaber ved arv, bør du også vide, hvordan og hvornår de bruges korrekt. Følgende er nogle ting, du skal huske på, når du overvejer en klasse-struktur baseret på arv:

Balance mellem enkelhed og fleksibilitet: Konstruktør-overbelastning giver mulighed for at have mange forskellige konstruktører, der tager forskellige typer argumenter, men overdreven brug kan gøre koden mere kompliceret og svær at vedligeholde. Det er god praksis at holde klassekoden kort, præcis og bekvem. Undgå at lave for mange konstruktører for en klasse for at opretholde en balance mellem enkelhed og fleksibilitet.

Hold konstruktører enkle: Konstruktører bør primært være ansvarlige for at initialisere et objekt med grundlæggende data. Det er god praksis at undgå unødvendig behandling og kompleks logik i en konstruktør. Hvis der er behov for beregning eller logik, er det bedre at oprette en separat metode til det.

;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 vigtige attributter: det er nødvendigt at initialisere alle vigtige attributter for et objekt med korrekte værdier for at sikre, at de fungerer korrekt – selv hvis det er en konstruktør uden 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 funktion gør det muligt at oprette flere konstruktører for en klasse?

2. Du kan få brug for at anvende et af koncepterne fra de foregående afsnit i denne quiz. Koden nedenfor indeholder en fejl på linje 15 og 16. Undersøg koden grundigt og afgør, hvad der er en effektiv løsning på denne fejl:

question mark

Hvilken funktion gør det muligt at oprette flere konstruktører for en klasse?

Select the correct answer

question mark

Du kan få brug for at anvende et af koncepterne fra de foregående afsnit i denne quiz. Koden nedenfor indeholder en fejl på linje 15 og 16. Undersøg koden grundigt og afgør, hvad der er en effektiv løsning på denne fejl:

Select the correct answer

Var alt klart?

Hvordan kan vi forbedre det?

Tak for dine kommentarer!

Sektion 5. Kapitel 2
some-alt