Arv
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
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(); } }
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.
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
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
12345public 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
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
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
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); } }
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
12345678910class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }
Eksempel:
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); } }
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
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"); } }
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
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; } }
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
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
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
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 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
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; } } }
God praksis:
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; } } }
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
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"); } }
God praksis:
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. 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:
Tak for dine kommentarer!
Spørg AI
Spørg AI
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
Arv
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
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(); } }
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.
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
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
12345public 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
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
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
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); } }
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
12345678910class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }
Eksempel:
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); } }
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
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"); } }
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
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; } }
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
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
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
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 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
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; } } }
God praksis:
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; } } }
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
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"); } }
God praksis:
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. 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:
Tak for dine kommentarer!