Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lernen Vererbung | OOP-Prinzipien
C# Jenseits der Grundlagen

bookVererbung

Im vorherigen Abschnitt haben Sie das Konzept der abgeleiteten Klassen betrachtet. Diese Eigenschaft einer Klasse, Eigenschaften von einer anderen Klasse zu erben, wird als Vererbung bezeichnet.

Obwohl Sie das Konzept der Vererbung bereits kennen, werden Sie es diesmal etwas umfassender durchgehen, um ein tieferes Verständnis zu erlangen.

Zur schnellen Wiederholung folgt ein Beispiel für Vererbung:

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
Hinweis

Die Direktive #pragma warning disable "warning code" kann verwendet werden, um bestimmte Compiler-Warnungen zu deaktivieren. Es wird im Allgemeinen davon abgeraten, Warnungen zu deaktivieren, da das Ignorieren zu unerwartetem Verhalten im Programm führen kann.

Der obige Code enthält eine Elternklasse namens Mammal sowie zwei abgeleitete Klassen namens Cat und Dog.

Beachte, dass keine der Klassen einen explizit definierten Konstruktor besitzt. Das bedeutet, dass beim Erstellen eines Objekts ein Standardkonstruktor verwendet wird.

Note
Hinweis

Ein Standardkonstruktor wird automatisch von der Programmiersprache bereitgestellt, wenn eine Klasse keinen explizit definierten Konstruktor besitzt. Ein Standardkonstruktor ist im Grunde ein leerer Konstruktor, der keinen Code enthält, zum Beispiel könnte public className() {} wie ein Standardkonstruktor aussehen. Da keine Attribute explizit initialisiert werden, erhalten alle Attribute Standardwerte – auch Nullwerte genannt.

Erstellen wir nun manuell einen Konstruktor für die Klasse Mammal, der ein Mammal-Objekt mit bestimmten Daten initialisiert:

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

Wenn Sie versuchen, dieses Programm zu kompilieren, werden im Konsolenfenster einige Fehler angezeigt. Um diese Fehler zu verstehen, müssen Sie zunächst zwei wichtige Konzepte im Zusammenhang mit Konstruktoren kennen.

Das erste ist, dass eine Klasse, sobald Sie explizit einen Konstruktor definieren, keinen Standardkonstruktor mehr besitzt. Der explizit definierte Konstruktor wird somit zum Hauptkonstruktor dieser Klasse, was in diesem Fall folgender ist:

index.cs

index.cs

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

Daher müssen beim Erstellen eines neuen Objekts immer die erforderlichen Argumente des Konstruktors in der richtigen Reihenfolge übergeben werden:

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

Zweitens können abgeleitete Klassen ebenfalls Konstruktoren besitzen, jedoch wird vor dem Aufruf des Konstruktors einer abgeleiteten Klasse auch der Konstruktor der Basisklasse (Elternklasse) aufgerufen:

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

Beim Ausführen dieses Codes wird deutlich, dass die Methode WriteLine() aus dem Konstruktor der Klasse 'Mammal', also der Elternklasse, automatisch aufgerufen wird. Dies bedeutet, dass es eine Regel gibt, nach der der Konstruktor der Basisklasse (auch Basiskonstruktor genannt) immer vor dem Konstruktor der abgeleiteten Klasse aufgerufen wird.

Diese Regel gilt ebenfalls bei mehrstufiger Vererbung:

Im obigen Diagramm ruft der Konstruktor von Kitten zunächst den Konstruktor von Cat auf, jedoch da Cat ebenfalls eine abgeleitete Klasse ist, ruft dieser vor sich selbst den Konstruktor von Mammal auf, und Mammal wiederum ruft vor seinem eigenen Konstruktor den Konstruktor von Animal auf. Daher wird insgesamt zuerst der Konstruktor der Superklasse ausgeführt – also der Konstruktor der Animal-Klasse – und anschließend erfolgt die Ausführung in absteigender Reihenfolge.

Wenn der Konstruktor der Elternklasse keine Argumente erwartet, wird er automatisch vom Compiler aufgerufen. Aus diesem Grund wurde im obigen Beispiel der Konstruktor von 'Mammal' automatisch aufgerufen. Schauen wir uns jedoch noch einmal den fehlerhaften Code an:

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

Im obigen Code treten zwei Fehler auf, die im Wesentlichen bedeuten, dass die Basiskonstruktoren nicht manuell aufgerufen wurden – da sie einige Argumente benötigen, müssen sie manuell aufgerufen werden. Die grundlegende Syntax zum manuellen Aufruf des Konstruktors der Elternklasse ist folgende:

index.cs

index.cs

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

Beispiel:

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

Mit dieser Syntax können alle erforderlichen Daten über die Konstruktoren von Mammal und Cat an den Konstruktor von Dog übergeben werden, um den zuvor aufgetretenen Fehler zu beheben:

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

Ein weiteres wichtiges Merkmal von Konstruktoren ist, dass diese wie andere Methoden überladen werden können. Es können mehrere Konstruktoren mit unterschiedlicher Anzahl an Argumenten erstellt werden:

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

In diesem Fall verfügt die Klasse Mammal über 3 Konstruktoren. Ein Säugetierobjekt kann also auf 3 verschiedene Arten initialisiert oder erstellt werden, und der Compiler wählt den passenden Konstruktor basierend auf Anzahl und Typ der Argumente aus:

index.cs

index.cs

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

Das bedeutet auch, dass Sie jeden der drei Konstruktoren aus den Konstruktoren der abgeleiteten Klasse aufrufen können. Zum Beispiel sind alle diese Varianten gültig:

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

Fügen wir die beiden obigen Codeausschnitte zusammen und ergänzen einige Console.WriteLine-Anweisungen, um zu sehen, in welcher Reihenfolge die Konstruktoren ausgeführt werden und die Ergebnisse praktisch nachzuvollziehen:

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

Nachdem Sie nun die verschiedenen Merkmale der Vererbung kennen, sollten Sie auch wissen, wie und wann Sie diese korrekt einsetzen. Im Folgenden sind einige Punkte aufgeführt, die bei der Überlegung zu einer vererbungsbasierten Klassenstruktur zu beachten sind:

Ausgewogenheit zwischen Einfachheit und Flexibilität: Konstruktorüberladung ermöglicht es, viele verschiedene Konstruktoren mit unterschiedlichen Argumenttypen zu definieren. Ein Übermaß an Konstruktoren kann jedoch den Code komplizierter und schwerer wartbar machen. Es ist bewährte Praxis, den Klassencode kurz, prägnant und übersichtlich zu halten. Vermeiden Sie zu viele Konstruktoren in einer Klasse, um ein Gleichgewicht zwischen Einfachheit und Flexibilität zu wahren.

Konstruktoren einfach halten: Konstruktoren sollten hauptsächlich für die Initialisierung eines Objekts mit Basisdaten verantwortlich sein. Es ist bewährte Praxis, unnötige Verarbeitung und komplexe Logik im Konstruktor zu vermeiden. Falls Berechnungen oder Logik erforderlich sind, empfiehlt es sich, dafür eine separate Methode zu erstellen.

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

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

Wichtige Attribute initialisieren: Es ist erforderlich, alle wichtigen Attribute eines Objekts mit korrekten Werten zu initialisieren, um deren ordnungsgemäße Funktion sicherzustellen – selbst bei einem Konstruktor ohne Argumente.

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

Gute 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. Welche Eigenschaft ermöglicht es, mehrere Konstruktoren für eine Klasse zu erstellen?

2. Möglicherweise muss in diesem Quiz eines der Konzepte aus den vorherigen Abschnitten angewendet werden. Der folgende Code enthält einen Fehler in den Zeilen 15 und 16. Analysieren Sie den Code sorgfältig und entscheiden Sie, welche effiziente Lösung für diesen Fehler geeignet ist:

question mark

Welche Eigenschaft ermöglicht es, mehrere Konstruktoren für eine Klasse zu erstellen?

Select the correct answer

question mark

Möglicherweise muss in diesem Quiz eines der Konzepte aus den vorherigen Abschnitten angewendet werden. Der folgende Code enthält einen Fehler in den Zeilen 15 und 16. Analysieren Sie den Code sorgfältig und entscheiden Sie, welche effiziente Lösung für diesen Fehler geeignet ist:

Select the correct answer

War alles klar?

Wie können wir es verbessern?

Danke für Ihr Feedback!

Abschnitt 5. Kapitel 2

Fragen Sie AI

expand

Fragen Sie AI

ChatGPT

Fragen Sie alles oder probieren Sie eine der vorgeschlagenen Fragen, um unser Gespräch zu beginnen

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

bookVererbung

Swipe um das Menü anzuzeigen

Im vorherigen Abschnitt haben Sie das Konzept der abgeleiteten Klassen betrachtet. Diese Eigenschaft einer Klasse, Eigenschaften von einer anderen Klasse zu erben, wird als Vererbung bezeichnet.

Obwohl Sie das Konzept der Vererbung bereits kennen, werden Sie es diesmal etwas umfassender durchgehen, um ein tieferes Verständnis zu erlangen.

Zur schnellen Wiederholung folgt ein Beispiel für Vererbung:

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
Hinweis

Die Direktive #pragma warning disable "warning code" kann verwendet werden, um bestimmte Compiler-Warnungen zu deaktivieren. Es wird im Allgemeinen davon abgeraten, Warnungen zu deaktivieren, da das Ignorieren zu unerwartetem Verhalten im Programm führen kann.

Der obige Code enthält eine Elternklasse namens Mammal sowie zwei abgeleitete Klassen namens Cat und Dog.

Beachte, dass keine der Klassen einen explizit definierten Konstruktor besitzt. Das bedeutet, dass beim Erstellen eines Objekts ein Standardkonstruktor verwendet wird.

Note
Hinweis

Ein Standardkonstruktor wird automatisch von der Programmiersprache bereitgestellt, wenn eine Klasse keinen explizit definierten Konstruktor besitzt. Ein Standardkonstruktor ist im Grunde ein leerer Konstruktor, der keinen Code enthält, zum Beispiel könnte public className() {} wie ein Standardkonstruktor aussehen. Da keine Attribute explizit initialisiert werden, erhalten alle Attribute Standardwerte – auch Nullwerte genannt.

Erstellen wir nun manuell einen Konstruktor für die Klasse Mammal, der ein Mammal-Objekt mit bestimmten Daten initialisiert:

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

Wenn Sie versuchen, dieses Programm zu kompilieren, werden im Konsolenfenster einige Fehler angezeigt. Um diese Fehler zu verstehen, müssen Sie zunächst zwei wichtige Konzepte im Zusammenhang mit Konstruktoren kennen.

Das erste ist, dass eine Klasse, sobald Sie explizit einen Konstruktor definieren, keinen Standardkonstruktor mehr besitzt. Der explizit definierte Konstruktor wird somit zum Hauptkonstruktor dieser Klasse, was in diesem Fall folgender ist:

index.cs

index.cs

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

Daher müssen beim Erstellen eines neuen Objekts immer die erforderlichen Argumente des Konstruktors in der richtigen Reihenfolge übergeben werden:

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

Zweitens können abgeleitete Klassen ebenfalls Konstruktoren besitzen, jedoch wird vor dem Aufruf des Konstruktors einer abgeleiteten Klasse auch der Konstruktor der Basisklasse (Elternklasse) aufgerufen:

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

Beim Ausführen dieses Codes wird deutlich, dass die Methode WriteLine() aus dem Konstruktor der Klasse 'Mammal', also der Elternklasse, automatisch aufgerufen wird. Dies bedeutet, dass es eine Regel gibt, nach der der Konstruktor der Basisklasse (auch Basiskonstruktor genannt) immer vor dem Konstruktor der abgeleiteten Klasse aufgerufen wird.

Diese Regel gilt ebenfalls bei mehrstufiger Vererbung:

Im obigen Diagramm ruft der Konstruktor von Kitten zunächst den Konstruktor von Cat auf, jedoch da Cat ebenfalls eine abgeleitete Klasse ist, ruft dieser vor sich selbst den Konstruktor von Mammal auf, und Mammal wiederum ruft vor seinem eigenen Konstruktor den Konstruktor von Animal auf. Daher wird insgesamt zuerst der Konstruktor der Superklasse ausgeführt – also der Konstruktor der Animal-Klasse – und anschließend erfolgt die Ausführung in absteigender Reihenfolge.

Wenn der Konstruktor der Elternklasse keine Argumente erwartet, wird er automatisch vom Compiler aufgerufen. Aus diesem Grund wurde im obigen Beispiel der Konstruktor von 'Mammal' automatisch aufgerufen. Schauen wir uns jedoch noch einmal den fehlerhaften Code an:

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

Im obigen Code treten zwei Fehler auf, die im Wesentlichen bedeuten, dass die Basiskonstruktoren nicht manuell aufgerufen wurden – da sie einige Argumente benötigen, müssen sie manuell aufgerufen werden. Die grundlegende Syntax zum manuellen Aufruf des Konstruktors der Elternklasse ist folgende:

index.cs

index.cs

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

Beispiel:

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

Mit dieser Syntax können alle erforderlichen Daten über die Konstruktoren von Mammal und Cat an den Konstruktor von Dog übergeben werden, um den zuvor aufgetretenen Fehler zu beheben:

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

Ein weiteres wichtiges Merkmal von Konstruktoren ist, dass diese wie andere Methoden überladen werden können. Es können mehrere Konstruktoren mit unterschiedlicher Anzahl an Argumenten erstellt werden:

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

In diesem Fall verfügt die Klasse Mammal über 3 Konstruktoren. Ein Säugetierobjekt kann also auf 3 verschiedene Arten initialisiert oder erstellt werden, und der Compiler wählt den passenden Konstruktor basierend auf Anzahl und Typ der Argumente aus:

index.cs

index.cs

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

Das bedeutet auch, dass Sie jeden der drei Konstruktoren aus den Konstruktoren der abgeleiteten Klasse aufrufen können. Zum Beispiel sind alle diese Varianten gültig:

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

Fügen wir die beiden obigen Codeausschnitte zusammen und ergänzen einige Console.WriteLine-Anweisungen, um zu sehen, in welcher Reihenfolge die Konstruktoren ausgeführt werden und die Ergebnisse praktisch nachzuvollziehen:

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

Nachdem Sie nun die verschiedenen Merkmale der Vererbung kennen, sollten Sie auch wissen, wie und wann Sie diese korrekt einsetzen. Im Folgenden sind einige Punkte aufgeführt, die bei der Überlegung zu einer vererbungsbasierten Klassenstruktur zu beachten sind:

Ausgewogenheit zwischen Einfachheit und Flexibilität: Konstruktorüberladung ermöglicht es, viele verschiedene Konstruktoren mit unterschiedlichen Argumenttypen zu definieren. Ein Übermaß an Konstruktoren kann jedoch den Code komplizierter und schwerer wartbar machen. Es ist bewährte Praxis, den Klassencode kurz, prägnant und übersichtlich zu halten. Vermeiden Sie zu viele Konstruktoren in einer Klasse, um ein Gleichgewicht zwischen Einfachheit und Flexibilität zu wahren.

Konstruktoren einfach halten: Konstruktoren sollten hauptsächlich für die Initialisierung eines Objekts mit Basisdaten verantwortlich sein. Es ist bewährte Praxis, unnötige Verarbeitung und komplexe Logik im Konstruktor zu vermeiden. Falls Berechnungen oder Logik erforderlich sind, empfiehlt es sich, dafür eine separate Methode zu erstellen.

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

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

Wichtige Attribute initialisieren: Es ist erforderlich, alle wichtigen Attribute eines Objekts mit korrekten Werten zu initialisieren, um deren ordnungsgemäße Funktion sicherzustellen – selbst bei einem Konstruktor ohne Argumente.

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

Gute 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. Welche Eigenschaft ermöglicht es, mehrere Konstruktoren für eine Klasse zu erstellen?

2. Möglicherweise muss in diesem Quiz eines der Konzepte aus den vorherigen Abschnitten angewendet werden. Der folgende Code enthält einen Fehler in den Zeilen 15 und 16. Analysieren Sie den Code sorgfältig und entscheiden Sie, welche effiziente Lösung für diesen Fehler geeignet ist:

question mark

Welche Eigenschaft ermöglicht es, mehrere Konstruktoren für eine Klasse zu erstellen?

Select the correct answer

question mark

Möglicherweise muss in diesem Quiz eines der Konzepte aus den vorherigen Abschnitten angewendet werden. Der folgende Code enthält einen Fehler in den Zeilen 15 und 16. Analysieren Sie den Code sorgfältig und entscheiden Sie, welche effiziente Lösung für diesen Fehler geeignet ist:

Select the correct answer

War alles klar?

Wie können wir es verbessern?

Danke für Ihr Feedback!

Abschnitt 5. Kapitel 2
some-alt