Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lära Arv | OOP-principer
C# Bortom Grunderna

bookArv

Du tittade på konceptet med deriverade klasser i föregående avsnitt. Denna egenskap hos en klass att ärva egenskaper från en annan klass kallas arv.

Även om du redan känner till konceptet arv, kommer du att gå igenom det på ett något mer omfattande sätt denna gång för att förstå det mer ingående.

Som en snabb repetition följer här ett exempel 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
Notering

Direktivet #pragma warning disable "warning code" kan användas för att inaktivera vissa kompilatorvarningar. Det är generellt avrått att inaktivera varningar, eftersom att ignorera dem kan leda till oväntat beteende i programmet.

Koden ovan innehåller en basklass kallad Mammal och två underklasser kallade Cat och Dog.

Observera att ingen av klasserna har någon explicit definierad konstruktor, vilket innebär att dessa klasser kommer att använda en standardkonstruktor när ett objekt skapas.

Note
Notering

En standardkonstruktor tillhandahålls automatiskt av programmeringsspråket om en klass inte har några explicit definierade konstruktorer. En standardkonstruktor är i princip en tom konstruktor som inte innehåller någon kod, till exempel public className() {} är hur en standardkonstruktor kan se ut. Eftersom den inte initierar några attribut explicit får alla attribut standardvärden – även kallade nollvärden.

Låt oss skapa en konstruktor för klassen Mammal manuellt, som initierar ett Mammal-objekt med viss 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); } }

Om du försöker kompilera detta program kommer det att visas några fel i konsolen. För att förstå dessa fel behöver du först förstå två viktiga koncept relaterade till konstruktorer.

Det första är att när du explicit definierar en konstruktor för en klass, har den klassen inte längre någon standardkonstruktor, och därmed blir den explicit definierade konstruktorn huvudkonstruktorn för den klassen, vilket i detta fall är:

index.cs

index.cs

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

Därför måste du alltid ange de obligatoriska argumenten till konstruktorn i rätt ordning när du skapar ett nytt 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);

För det andra kan härledda klasser också ha konstruktorer, men innan konstruktorn för en härledd klass anropas, anropas även basklassens (förälderns) konstruktor:

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ör denna kod ser du att metoden WriteLine() från 'Mammal'-konstruktorn, som är basklassen, automatiskt anropas. Detta innebär att det är en regel att basklassens konstruktor (även kallad baskonstruktor) alltid anropas före den ärvda klassens konstruktor.

Denna regel gäller även vid flernivåarv:

I diagrammet ovan anropar Kitten-konstruktorn Cat-konstruktorn före sin egen, men eftersom Cat också är en underklass anropar den Mammal-konstruktorn före sig själv, och Mammal anropar Animal-konstruktorn före sin egen konstruktor. Därför är den första konstruktorn som exekveras konstruktorn för superklassen — det vill säga Animal-klassens konstruktor, och därefter fortsätter det nedåt.

Om basklassens konstruktor inte tar några argument anropas den automatiskt av kompilatorn, vilket är anledningen till att 'Mammal'-konstruktorn i exemplet ovan anropades automatiskt. Låt oss dock titta på den felaktiga koden 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 koden ovan får du två felmeddelanden som i princip betyder att du inte manuellt har anropat baskonstruktorn — eftersom den kräver argument måste du anropa den manuellt. Den grundläggande syntaxen för att manuellt anropa basklassens konstruktor är följande:

index.cs

index.cs

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

Exempel:

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

Med denna syntax kan all nödvändig data skickas till Mammal-konstruktorn via Cat- och Dog-konstruktörerna för att åtgärda det fel som uppstod tidigare:

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 annan viktig egenskap hos konstruktörer är att de kan överlagras precis som vi överlagrar andra metoder. Det går att skapa flera konstruktörer med olika antal argument:

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 detta fall har klassen Mammal tre konstruktorer. Du kan alltså initiera eller skapa ett däggdjursobjekt på tre olika sätt, och kompilatorn väljer vilken konstruktor som ska anropas baserat på antal och typ av argument:

index.cs

index.cs

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

Detta innebär också att du kan anropa någon av de tre konstruktorerna från den ärvda klassens konstruktorer. Till exempel är alla dessa giltiga:

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

Låt oss sammanfoga de två ovanstående kodsnuttarna och lägga till några Console.WriteLine-satser för att se i vilken ordning konstruktorerna körs och praktiskt se resultaten:

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 när du känner till olika egenskaper hos arv bör du också veta hur och när de ska användas korrekt. Följande är några saker att tänka på när du överväger en arvbaserad klassstruktur:

Balans mellan enkelhet och flexibilitet: Konstruktoröverlagring möjliggör flera olika konstruktorer med olika typer av argument, men att överanvända detta kan göra koden mer komplicerad och svår att underhålla. Det är en god praxis att hålla klasskoden kort, koncis och lättanvänd. Undvik att skapa för många konstruktorer för en klass för att bibehålla balansen mellan enkelhet och flexibilitet.

Håll konstruktorer enkla: Konstruktorer bör huvudsakligen ansvara för att initiera ett objekt med grundläggande data. Det är en god praxis att undvika onödig bearbetning och komplex logik i en konstruktor. Om någon beräkning eller logik behövs är det bättre att skapa en separat metod för detta.

;Dålig praxis:;

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 praxis:

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

Initiera viktiga attribut: det är nödvändigt att initiera alla viktiga attribut för ett objekt med korrekta värden för att säkerställa att de fungerar korrekt – även om det är en konstruktor utan argument.

Dålig praxis:

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 praxis:

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. Vilken funktion gör det möjligt att skapa flera konstruktorer för en klass?

2. Du kan behöva använda ett av koncepten från tidigare avsnitt i detta quiz. Koden nedan har ett fel på rad 15 och 16. Granska koden noggrant och avgör vilken åtgärd som är mest effektiv för att rätta till felet:

question mark

Vilken funktion gör det möjligt att skapa flera konstruktorer för en klass?

Select the correct answer

question mark

Du kan behöva använda ett av koncepten från tidigare avsnitt i detta quiz. Koden nedan har ett fel på rad 15 och 16. Granska koden noggrant och avgör vilken åtgärd som är mest effektiv för att rätta till felet:

Select the correct answer

Var allt tydligt?

Hur kan vi förbättra det?

Tack för dina kommentarer!

Avsnitt 5. Kapitel 2

Fråga AI

expand

Fråga AI

ChatGPT

Fråga vad du vill eller prova någon av de föreslagna frågorna för att starta vårt samtal

Suggested prompts:

Can you explain more about constructor overloading?

What happens if I don't call the base constructor in a derived class?

Can you give an example of multilevel inheritance with constructors?

Awesome!

Completion rate improved to 2.04

bookArv

Svep för att visa menyn

Du tittade på konceptet med deriverade klasser i föregående avsnitt. Denna egenskap hos en klass att ärva egenskaper från en annan klass kallas arv.

Även om du redan känner till konceptet arv, kommer du att gå igenom det på ett något mer omfattande sätt denna gång för att förstå det mer ingående.

Som en snabb repetition följer här ett exempel 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
Notering

Direktivet #pragma warning disable "warning code" kan användas för att inaktivera vissa kompilatorvarningar. Det är generellt avrått att inaktivera varningar, eftersom att ignorera dem kan leda till oväntat beteende i programmet.

Koden ovan innehåller en basklass kallad Mammal och två underklasser kallade Cat och Dog.

Observera att ingen av klasserna har någon explicit definierad konstruktor, vilket innebär att dessa klasser kommer att använda en standardkonstruktor när ett objekt skapas.

Note
Notering

En standardkonstruktor tillhandahålls automatiskt av programmeringsspråket om en klass inte har några explicit definierade konstruktorer. En standardkonstruktor är i princip en tom konstruktor som inte innehåller någon kod, till exempel public className() {} är hur en standardkonstruktor kan se ut. Eftersom den inte initierar några attribut explicit får alla attribut standardvärden – även kallade nollvärden.

Låt oss skapa en konstruktor för klassen Mammal manuellt, som initierar ett Mammal-objekt med viss 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); } }

Om du försöker kompilera detta program kommer det att visas några fel i konsolen. För att förstå dessa fel behöver du först förstå två viktiga koncept relaterade till konstruktorer.

Det första är att när du explicit definierar en konstruktor för en klass, har den klassen inte längre någon standardkonstruktor, och därmed blir den explicit definierade konstruktorn huvudkonstruktorn för den klassen, vilket i detta fall är:

index.cs

index.cs

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

Därför måste du alltid ange de obligatoriska argumenten till konstruktorn i rätt ordning när du skapar ett nytt 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);

För det andra kan härledda klasser också ha konstruktorer, men innan konstruktorn för en härledd klass anropas, anropas även basklassens (förälderns) konstruktor:

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ör denna kod ser du att metoden WriteLine() från 'Mammal'-konstruktorn, som är basklassen, automatiskt anropas. Detta innebär att det är en regel att basklassens konstruktor (även kallad baskonstruktor) alltid anropas före den ärvda klassens konstruktor.

Denna regel gäller även vid flernivåarv:

I diagrammet ovan anropar Kitten-konstruktorn Cat-konstruktorn före sin egen, men eftersom Cat också är en underklass anropar den Mammal-konstruktorn före sig själv, och Mammal anropar Animal-konstruktorn före sin egen konstruktor. Därför är den första konstruktorn som exekveras konstruktorn för superklassen — det vill säga Animal-klassens konstruktor, och därefter fortsätter det nedåt.

Om basklassens konstruktor inte tar några argument anropas den automatiskt av kompilatorn, vilket är anledningen till att 'Mammal'-konstruktorn i exemplet ovan anropades automatiskt. Låt oss dock titta på den felaktiga koden 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 koden ovan får du två felmeddelanden som i princip betyder att du inte manuellt har anropat baskonstruktorn — eftersom den kräver argument måste du anropa den manuellt. Den grundläggande syntaxen för att manuellt anropa basklassens konstruktor är följande:

index.cs

index.cs

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

Exempel:

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

Med denna syntax kan all nödvändig data skickas till Mammal-konstruktorn via Cat- och Dog-konstruktörerna för att åtgärda det fel som uppstod tidigare:

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 annan viktig egenskap hos konstruktörer är att de kan överlagras precis som vi överlagrar andra metoder. Det går att skapa flera konstruktörer med olika antal argument:

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 detta fall har klassen Mammal tre konstruktorer. Du kan alltså initiera eller skapa ett däggdjursobjekt på tre olika sätt, och kompilatorn väljer vilken konstruktor som ska anropas baserat på antal och typ av argument:

index.cs

index.cs

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

Detta innebär också att du kan anropa någon av de tre konstruktorerna från den ärvda klassens konstruktorer. Till exempel är alla dessa giltiga:

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

Låt oss sammanfoga de två ovanstående kodsnuttarna och lägga till några Console.WriteLine-satser för att se i vilken ordning konstruktorerna körs och praktiskt se resultaten:

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 när du känner till olika egenskaper hos arv bör du också veta hur och när de ska användas korrekt. Följande är några saker att tänka på när du överväger en arvbaserad klassstruktur:

Balans mellan enkelhet och flexibilitet: Konstruktoröverlagring möjliggör flera olika konstruktorer med olika typer av argument, men att överanvända detta kan göra koden mer komplicerad och svår att underhålla. Det är en god praxis att hålla klasskoden kort, koncis och lättanvänd. Undvik att skapa för många konstruktorer för en klass för att bibehålla balansen mellan enkelhet och flexibilitet.

Håll konstruktorer enkla: Konstruktorer bör huvudsakligen ansvara för att initiera ett objekt med grundläggande data. Det är en god praxis att undvika onödig bearbetning och komplex logik i en konstruktor. Om någon beräkning eller logik behövs är det bättre att skapa en separat metod för detta.

;Dålig praxis:;

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 praxis:

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

Initiera viktiga attribut: det är nödvändigt att initiera alla viktiga attribut för ett objekt med korrekta värden för att säkerställa att de fungerar korrekt – även om det är en konstruktor utan argument.

Dålig praxis:

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 praxis:

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. Vilken funktion gör det möjligt att skapa flera konstruktorer för en klass?

2. Du kan behöva använda ett av koncepten från tidigare avsnitt i detta quiz. Koden nedan har ett fel på rad 15 och 16. Granska koden noggrant och avgör vilken åtgärd som är mest effektiv för att rätta till felet:

question mark

Vilken funktion gör det möjligt att skapa flera konstruktorer för en klass?

Select the correct answer

question mark

Du kan behöva använda ett av koncepten från tidigare avsnitt i detta quiz. Koden nedan har ett fel på rad 15 och 16. Granska koden noggrant och avgör vilken åtgärd som är mest effektiv för att rätta till felet:

Select the correct answer

Var allt tydligt?

Hur kan vi förbättra det?

Tack för dina kommentarer!

Avsnitt 5. Kapitel 2
some-alt