Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Apprendre Héritage | Principes de la POO
C# au-delà des Bases
course content

Contenu du cours

C# au-delà des Bases

C# au-delà des Bases

1. Structures Supplémentaires et Gestion des Fichiers
2. Structures et Énumérateurs
3. Introduction à la Programmation Orientée Objet (POO)
4. Essentiels de la POO
5. Principes de la POO

book
Héritage

Nous avons examiné le concept de classes dérivées dans la dernière section. Cette fonctionnalité d'une classe pour hériter des propriétés d'une autre classe est appelée Héritage.

Bien que nous connaissions déjà le concept d'Héritage, nous allons l'aborder de manière un peu plus complète cette fois-ci pour le comprendre plus en profondeur.

Pour une révision rapide, voici un exemple d'Héritage :

cs

index

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

Le code ci-dessus contient une classe parente appelée Mammal et deux classes dérivées appelées Cat et Dog.

Notez qu'aucune des classes n'a de constructeur explicitement défini, ce qui signifie que ces classes utiliseront un constructeur par défaut lorsqu'un objet est créé.

Créons un constructeur pour la classe Mammal manuellement, qui initialise un objet Mammal avec certaines données :

cs

index

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

Si nous essayons de compiler ce programme, il affichera des erreurs dans la console. Pour comprendre ces erreurs, nous devons d'abord comprendre deux concepts importants liés aux constructeurs.

Le premier est qu'une fois que nous définissons explicitement un constructeur pour une classe, cette classe n'a plus de constructeur par défaut, et donc le constructeur défini explicitement devient le constructeur principal de cette classe qui, dans ce cas, est :

cs

index

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

Par conséquent, lors de la création d'un nouvel objet, nous devons toujours passer les arguments requis du constructeur dans le bon ordre :

cs

index

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

Deuxièmement, les classes dérivées peuvent également avoir des constructeurs, cependant avant que le constructeur d'une classe dérivée ne soit appelé, le constructeur de la base (parent) est également appelé :

cs

index

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

Lorsque nous exécutons ce code, nous voyons que la méthode WriteLine() du constructeur 'Mammal', qui est la classe parente, est automatiquement appelée. Ce qui signifie qu'il est une règle que le constructeur de la classe de base (également appelé le constructeur de base) est toujours appelé avant le constructeur de la classe dérivée.

Cette règle est également vraie dans le cas de l'héritage multiniveau :

Dans le diagramme ci-dessus, le constructeur Kitten appelle le constructeur Cat avant le sien, cependant, comme Cat est également une classe dérivée, il appelle le constructeur Mammal avant lui-même, et Mammal appelle le constructeur Animal avant son constructeur, donc globalement le premier constructeur qui est exécuté est le constructeur de la super classe - qui est le constructeur de la classe Animal et ensuite cela descend de là.

Si le constructeur de la classe parente ne prend aucun argument, il est automatiquement appelé par le compilateur automatiquement, c'est la raison pour laquelle le constructeur 'Mammal' dans l'exemple ci-dessus a été appelé automatiquement. Cependant, examinons à nouveau le code défectueux :

cs

index

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

Dans le code ci-dessus, nous obtenons deux erreurs qui signifient essentiellement que nous n'avons pas appelé manuellement les constructeurs de base - puisqu'il nécessite certains arguments, nous devons l'appeler manuellement. La syntaxe de base pour appeler manuellement le constructeur de la classe parente est la suivante :

cs

index

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

Exemple :

cs

index

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

En utilisant cette syntaxe, nous pouvons passer toutes les données requises au constructeur Mammal à travers les constructeurs Cat et Dog pour corriger l'erreur que nous avions auparavant :

cs

index

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

Une autre caractéristique importante des constructeurs est que nous pouvons surcharger les constructeurs tout comme nous surchargeons toute autre méthode. Nous pouvons créer plusieurs constructeurs avec un nombre d'arguments variable :

cs

index

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

Dans ce cas, la classe Mammal a 3 constructeurs. Nous pouvons donc initialiser ou créer un objet mammifère de 3 manières différentes et le compilateur choisira quel constructeur appeler en fonction du nombre et du type d'arguments :

cs

index

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

Cela signifie également que nous pouvons appeler n'importe lequel des 3 constructeurs à partir des constructeurs de la classe dérivée. Par exemple, tous ceux-ci sont valides :

cs

index

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

Assemblons les deux extraits ci-dessus et ajoutons quelques instructions Console.WriteLine pour voir dans quel ordre les constructeurs sont exécutés afin de voir pratiquement les résultats :

cs

index

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

Maintenant que vous connaissez les différentes fonctionnalités de l'héritage, vous devez également savoir comment ou quand les utiliser correctement. Voici quelques éléments à garder à l'esprit lors de la considération d'une structure de classe basée sur l'héritage :

Équilibre entre simplicité et flexibilité : La surcharge de constructeur vous permet d'avoir de nombreux constructeurs différents qui prennent différents types d'arguments, mais en abuser peut rendre le code plus compliqué et difficile à maintenir. Il est préférable de garder le code de la classe court, concis et pratique. Évitez de créer trop de constructeurs pour une classe afin de maintenir un équilibre entre simplicité et flexibilité.

Gardez les constructeurs simples : Les constructeurs doivent principalement être responsables de l'initialisation d'un objet avec des données de base. Il est préférable d'éviter les traitements inutiles et la logique complexe à l'intérieur d'un constructeur. Si un calcul ou une logique est nécessaire, il est préférable de créer une méthode séparée pour cela.

Mauvaise pratique :

cs

index

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

Bonne Pratique:

cs

index

copy
123456789101112131415161718192021222324252627282930
class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; this.balance = balance; monthlyInterest(); } // This method might be used in other places too private void monthlyInterest() { if(accountType == "Savings") { // Plus 1 Percent balance += balance * 0.01; } else if(accountType == "HighYieldSavings") { // Plus 5 percent balance += balance * 0.05; } } }

Initialiser les attributs importants : Il est nécessaire d'initialiser tous les attributs importants d'un objet avec des valeurs correctes pour s'assurer qu'ils fonctionnent correctement - même s'il s'agit d'un constructeur sans arguments.

Mauvaise pratique :

cs

index

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

Bonne pratique :

cs

index

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. Quelle fonctionnalité nous permet de créer plusieurs constructeurs pour une classe ?

2. Vous pourriez avoir besoin d'utiliser l'un des concepts des sections précédentes dans ce quiz. Le code ci-dessous a une erreur aux lignes 15 et 16. Regardez attentivement le code et décidez quelle est une solution efficace pour cette erreur :

Quelle fonctionnalité nous permet de créer plusieurs constructeurs pour une classe ?

Quelle fonctionnalité nous permet de créer plusieurs constructeurs pour une classe ?

Sélectionnez la réponse correcte

Vous pourriez avoir besoin d'utiliser l'un des concepts des sections précédentes dans ce quiz. Le code ci-dessous a une erreur aux lignes 15 et 16. Regardez attentivement le code et décidez quelle est une solution efficace pour cette erreur :

Vous pourriez avoir besoin d'utiliser l'un des concepts des sections précédentes dans ce quiz. Le code ci-dessous a une erreur aux lignes 15 et 16. Regardez attentivement le code et décidez quelle est une solution efficace pour cette erreur :

Sélectionnez la réponse correcte

Tout était clair ?

Comment pouvons-nous l'améliorer ?

Merci pour vos commentaires !

Section 5. Chapitre 2
We're sorry to hear that something went wrong. What happened?
some-alt