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

bookHéritage

Vous avez étudié le concept de classes dérivées dans la section précédente. Cette fonctionnalité d'une classe à hériter des propriétés d'une autre classe s'appelle héritage.

Bien que vous connaissiez déjà le concept d'héritage, vous l'aborderez cette fois de manière un peu plus approfondie afin de mieux le comprendre.

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

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
Remarque

La directive #pragma warning disable "warning code" peut être utilisée pour désactiver certains avertissements du compilateur. Il est généralement déconseillé de désactiver les avertissements, car les ignorer peut entraîner un comportement inattendu du programme.

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 lors de la création d'un objet.

Note
Remarque

Un constructeur par défaut est automatiquement fourni par le langage de programmation si une classe ne possède aucun constructeur explicitement défini. Un constructeur par défaut est essentiellement un constructeur vide qui ne contient aucun code, par exemple public className() {} est ce à quoi pourrait ressembler un constructeur par défaut. Puisqu'il n'initialise explicitement aucun attribut, tous les attributs prennent des valeurs par défaut - également appelées valeurs nulles.

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

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

Si vous essayez de compiler ce programme, des erreurs apparaîtront dans la console. Pour comprendre ces erreurs, il est nécessaire de saisir deux concepts importants liés aux constructeurs.

Le premier est que, dès que vous définissez explicitement un constructeur pour une classe, cette classe ne possède plus de constructeur par défaut. Ainsi, le constructeur défini explicitement devient le constructeur principal de cette classe, qui dans ce cas est :

index.cs

index.cs

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, il est toujours nécessaire de fournir les arguments requis du constructeur dans le bon ordre :

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

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 classe de base (parente) est également appelé :

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

Lorsque vous exécutez ce code, vous constatez que la méthode WriteLine() du constructeur de 'Mammal', qui est la classe parente, est automatiquement appelée. Cela signifie qu'il s'agit d'une règle selon laquelle le constructeur de la classe de base (également appelé constructeur de base) est toujours appelé avant le constructeur de la classe dérivée.

Cette règle s'applique également dans le cas d'une héritage multiniveau :

Dans le schéma ci-dessus, le constructeur de Kitten appelle le constructeur de Cat avant le sien, cependant, puisque Cat est également une classe dérivée, il appelle le constructeur de Mammal avant le sien, et Mammal appelle le constructeur de Animal avant le sien. Ainsi, le premier constructeur exécuté est celui de la super classe — c'est-à-dire le constructeur de la classe Animal, puis l'exécution descend dans la hiérarchie.

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

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

Dans le code ci-dessus, deux erreurs apparaissent, indiquant essentiellement que les constructeurs de base n'ont pas été appelés manuellement — puisqu'ils nécessitent des arguments, il faut les appeler explicitement. La syntaxe de base pour appeler manuellement le constructeur de la classe parente est la suivante :

index.cs

index.cs

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

Exemple :

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

En utilisant cette syntaxe, il est possible de transmettre toutes les données requises au constructeur Mammal via les constructeurs Cat et Dog afin de corriger l’erreur rencontrée précédemment :

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

Une autre caractéristique importante des constructeurs est la possibilité de les surcharger, tout comme pour toute autre méthode. Il est possible de créer plusieurs constructeurs avec un nombre d’arguments différent :

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

Dans ce cas, la classe Mammal possède 3 constructeurs. Il est donc possible d'initialiser ou de 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 :

index.cs

index.cs

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

Cela signifie également que vous pouvez appeler n'importe lequel des 3 constructeurs depuis les constructeurs de la classe dérivée. Par exemple, toutes les options suivantes sont valides :

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

Assemblons les deux extraits ci-dessus et ajoutons quelques instructions Console.WriteLine afin d'observer dans quel ordre les constructeurs sont exécutés, pour constater les résultats de manière pratique :

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

Maintenant que vous connaissez les différentes fonctionnalités de l'héritage, il est également important de savoir comment et quand les utiliser correctement. Voici quelques points à garder à l'esprit lors de la conception d'une structure de classe basée sur l'héritage :

Équilibre entre simplicité et flexibilité : La surcharge de constructeurs permet d'avoir plusieurs constructeurs acceptant différents types d'arguments, mais en abuser peut rendre le code plus complexe et difficile à maintenir. Il est recommandé 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é.

Garder les constructeurs simples : Les constructeurs doivent principalement servir à initialiser un objet avec des données de base. Il est conseillé d'éviter tout traitement inutile et toute 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:;

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

Bonne pratique :

index.cs

index.cs

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

Initialiser les attributs importants : il est nécessaire d'initialiser tous les attributs importants d'un objet avec des valeurs correctes afin de garantir leur bon fonctionnement, même s'il s'agit d'un constructeur sans arguments.

Mauvaise pratique :

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

Bonne pratique :

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. Quelle fonctionnalité 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 comporte une erreur aux lignes 15 et 16. Examinez attentivement le code et décidez quelle est la correction la plus efficace pour cette erreur :

question mark

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

Select the correct answer

question mark

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

Select the correct answer

Tout était clair ?

Comment pouvons-nous l'améliorer ?

Merci pour vos commentaires !

Section 5. Chapitre 2

Demandez à l'IA

expand

Demandez à l'IA

ChatGPT

Posez n'importe quelle question ou essayez l'une des questions suggérées pour commencer notre discussion

Awesome!

Completion rate improved to 2.04

bookHéritage

Glissez pour afficher le menu

Vous avez étudié le concept de classes dérivées dans la section précédente. Cette fonctionnalité d'une classe à hériter des propriétés d'une autre classe s'appelle héritage.

Bien que vous connaissiez déjà le concept d'héritage, vous l'aborderez cette fois de manière un peu plus approfondie afin de mieux le comprendre.

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

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
Remarque

La directive #pragma warning disable "warning code" peut être utilisée pour désactiver certains avertissements du compilateur. Il est généralement déconseillé de désactiver les avertissements, car les ignorer peut entraîner un comportement inattendu du programme.

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 lors de la création d'un objet.

Note
Remarque

Un constructeur par défaut est automatiquement fourni par le langage de programmation si une classe ne possède aucun constructeur explicitement défini. Un constructeur par défaut est essentiellement un constructeur vide qui ne contient aucun code, par exemple public className() {} est ce à quoi pourrait ressembler un constructeur par défaut. Puisqu'il n'initialise explicitement aucun attribut, tous les attributs prennent des valeurs par défaut - également appelées valeurs nulles.

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

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

Si vous essayez de compiler ce programme, des erreurs apparaîtront dans la console. Pour comprendre ces erreurs, il est nécessaire de saisir deux concepts importants liés aux constructeurs.

Le premier est que, dès que vous définissez explicitement un constructeur pour une classe, cette classe ne possède plus de constructeur par défaut. Ainsi, le constructeur défini explicitement devient le constructeur principal de cette classe, qui dans ce cas est :

index.cs

index.cs

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, il est toujours nécessaire de fournir les arguments requis du constructeur dans le bon ordre :

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

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 classe de base (parente) est également appelé :

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

Lorsque vous exécutez ce code, vous constatez que la méthode WriteLine() du constructeur de 'Mammal', qui est la classe parente, est automatiquement appelée. Cela signifie qu'il s'agit d'une règle selon laquelle le constructeur de la classe de base (également appelé constructeur de base) est toujours appelé avant le constructeur de la classe dérivée.

Cette règle s'applique également dans le cas d'une héritage multiniveau :

Dans le schéma ci-dessus, le constructeur de Kitten appelle le constructeur de Cat avant le sien, cependant, puisque Cat est également une classe dérivée, il appelle le constructeur de Mammal avant le sien, et Mammal appelle le constructeur de Animal avant le sien. Ainsi, le premier constructeur exécuté est celui de la super classe — c'est-à-dire le constructeur de la classe Animal, puis l'exécution descend dans la hiérarchie.

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

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

Dans le code ci-dessus, deux erreurs apparaissent, indiquant essentiellement que les constructeurs de base n'ont pas été appelés manuellement — puisqu'ils nécessitent des arguments, il faut les appeler explicitement. La syntaxe de base pour appeler manuellement le constructeur de la classe parente est la suivante :

index.cs

index.cs

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

Exemple :

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

En utilisant cette syntaxe, il est possible de transmettre toutes les données requises au constructeur Mammal via les constructeurs Cat et Dog afin de corriger l’erreur rencontrée précédemment :

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

Une autre caractéristique importante des constructeurs est la possibilité de les surcharger, tout comme pour toute autre méthode. Il est possible de créer plusieurs constructeurs avec un nombre d’arguments différent :

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

Dans ce cas, la classe Mammal possède 3 constructeurs. Il est donc possible d'initialiser ou de 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 :

index.cs

index.cs

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

Cela signifie également que vous pouvez appeler n'importe lequel des 3 constructeurs depuis les constructeurs de la classe dérivée. Par exemple, toutes les options suivantes sont valides :

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

Assemblons les deux extraits ci-dessus et ajoutons quelques instructions Console.WriteLine afin d'observer dans quel ordre les constructeurs sont exécutés, pour constater les résultats de manière pratique :

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

Maintenant que vous connaissez les différentes fonctionnalités de l'héritage, il est également important de savoir comment et quand les utiliser correctement. Voici quelques points à garder à l'esprit lors de la conception d'une structure de classe basée sur l'héritage :

Équilibre entre simplicité et flexibilité : La surcharge de constructeurs permet d'avoir plusieurs constructeurs acceptant différents types d'arguments, mais en abuser peut rendre le code plus complexe et difficile à maintenir. Il est recommandé 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é.

Garder les constructeurs simples : Les constructeurs doivent principalement servir à initialiser un objet avec des données de base. Il est conseillé d'éviter tout traitement inutile et toute 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:;

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

Bonne pratique :

index.cs

index.cs

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

Initialiser les attributs importants : il est nécessaire d'initialiser tous les attributs importants d'un objet avec des valeurs correctes afin de garantir leur bon fonctionnement, même s'il s'agit d'un constructeur sans arguments.

Mauvaise pratique :

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

Bonne pratique :

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. Quelle fonctionnalité 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 comporte une erreur aux lignes 15 et 16. Examinez attentivement le code et décidez quelle est la correction la plus efficace pour cette erreur :

question mark

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

Select the correct answer

question mark

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

Select the correct answer

Tout était clair ?

Comment pouvons-nous l'améliorer ?

Merci pour vos commentaires !

Section 5. Chapitre 2
some-alt