Ereditarietà
Nella sezione precedente hai esaminato il concetto di classi derivate. Questa caratteristica di una classe di ereditare proprietà da un'altra classe è chiamata Ereditarietà.
Anche se conosci già il concetto di Ereditarietà, questa volta lo approfondirai in modo leggermente più completo per comprenderlo a fondo.
Come rapido ripasso, di seguito è riportato un esempio di Ereditarietà:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kilogram (1 kg = 2.2 pounds) } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Cat myCat = new Cat(); Dog myDog = new Dog(); myCat.speak(); myDog.speak(); } }
La direttiva #pragma warning disable "warning code"
può essere utilizzata per disabilitare determinati avvisi del compilatore. In generale, è sconsigliato disabilitare gli avvisi, poiché ignorarli può portare a comportamenti imprevisti nel programma.
Il codice sopra contiene una classe genitore chiamata Mammal
e due classi derivate chiamate Cat
e Dog
.
Si noti che nessuna delle classi ha un costruttore esplicitamente definito, il che significa che queste classi utilizzeranno un costruttore predefinito quando viene creato un oggetto.
Un costruttore predefinito viene fornito automaticamente dal linguaggio di programmazione se una classe non ha costruttori esplicitamente definiti. Un costruttore predefinito è fondamentalmente un costruttore vuoto che non contiene alcun codice, ad esempio public className() {}
è come potrebbe apparire un costruttore predefinito. Poiché non inizializza esplicitamente alcun attributo, tutti gli attributi assumono valori predefiniti - chiamati anche valori zero.
Creazione manuale di un costruttore per la classe Mammal
, che inizializza un oggetto Mammal
con alcuni dati:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
Se si tenta di compilare questo programma, verranno visualizzati alcuni errori nella console. Per comprendere tali errori è necessario prima comprendere due concetti importanti relativi ai costruttori.
Il primo è che, una volta definito esplicitamente un costruttore per una classe, tale classe non dispone più di un costruttore predefinito e quindi il costruttore definito esplicitamente diventa il costruttore principale di quella classe, che in questo caso è:
index.cs
12345public Mammal(int age, float weight) { this.age = age; this.weight = weight; }
Pertanto, durante la creazione di un nuovo oggetto, è sempre necessario fornire gli argomenti richiesti dal costruttore nell'ordine corretto:
index.cs
1234567// Incorrect ways to create 'Mammal', will show an error Mammal m1 = new Mammal(); Mammal m1 = new Mammal(10); Mammal m1 = new Mammal(42.0f); // Correct way to create 'Mammal', will execute fine. Mammal m1 = new Mammal(10, 42.0f);
In secondo luogo, anche le classi derivate possono avere costruttori; tuttavia, prima che venga chiamato il costruttore di una classe derivata, viene chiamato anche il costruttore della classe base (genitore):
index.cs
1234567891011121314151617181920212223242526272829303132333435#pragma warning disable CS0169 // To disable some unnecessary warnings, using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg // No attribute is initialized explicitly in this constructor // Hence, all attributes will take up "zero" values // It is similar to a "default" constructor except it outputs a message public Mammal() { Console.WriteLine("Mammal Constructor Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog Constructor Called"); } } class ConsoleApp { static void Main() { Dog myDog = new Dog(); } }
Quando esegui questo codice, noterai che il metodo WriteLine()
dal costruttore di 'Mammal', che rappresenta la classe genitore, viene chiamato automaticamente. Questo significa che è una regola che il costruttore della classe base (chiamato anche costruttore base) venga sempre invocato prima del costruttore della classe derivata.
Questa regola vale anche nel caso di ereditarietà multilivello:

Nel diagramma sopra, il costruttore di Kitten
chiama il costruttore di Cat
prima del proprio; tuttavia, poiché anche Cat
è una classe derivata, richiama il costruttore di Mammal
prima di sé, e Mammal
richiama il costruttore di Animal
prima del proprio. Pertanto, il primo costruttore eseguito è quello della superclasse — ovvero il costruttore della classe Animal
— e poi si procede in discesa.
Se il costruttore della classe padre non accetta argomenti, viene chiamato automaticamente dal compilatore; per questo motivo il costruttore di 'Mammal' nell'esempio è stato chiamato automaticamente. Tuttavia, esaminiamo di nuovo il codice errato:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
Nel codice sopra si ricevono due errori che indicano sostanzialmente che non sono stati chiamati manualmente i costruttori base — poiché richiedono argomenti, è necessario richiamarli manualmente. La sintassi di base per invocare manualmente il costruttore della classe padre è la seguente:
index.cs
12345678910class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }
Esempio:
index.cs
1234567891011121314151617181920212223242526272829303132using System; class ExampleParentClass { int value1; int value2; public ExampleParentClass(int value1, int value2) { this.value1 = value1; } } class ExampleDerivedClass : ExampleParentClass { int value3; // The value1 and value2 arguments are passed to the base class's contructor public ExampleDerivedClass(int value1, int value2, int value3) : base (value1, value2) { this.value3 = value3; } } class ConsoleApp { static void Main() { var testObject = new ExampleDerivedClass(5, 7, 9); } }
Utilizzando questa sintassi, è possibile passare tutti i dati necessari al costruttore di Mammal
tramite i costruttori di Cat
e Dog
per correggere l'errore riscontrato in precedenza:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public Cat(int age, float weight, string furPattern) : base(age, weight) { this.furPattern = furPattern; } public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); // Creating a "Dog" object with some data Dog d1 = new Dog(10, 42.5f, "Dobermann"); Console.WriteLine("Executed Successfully"); } }
Un'altra caratteristica importante dei costruttori è che è possibile sovraccaricare i costruttori proprio come si sovraccarica qualsiasi altro metodo. È possibile creare più costruttori con un numero variabile di argomenti:
index.cs
12345678910111213141516171819202122232425class Mammal { int age; float weight; // kg // 1st constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }
In questo caso, la classe Mammal
dispone di 3 costruttori. È quindi possibile inizializzare o creare un oggetto mammifero in 3 modi diversi e il compilatore selezionerà quale costruttore chiamare in base al numero e al tipo di argomenti:
index.cs
1234// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);
Questo significa anche che puoi chiamare uno qualsiasi dei 3 costruttori dai costruttori della classe derivata. Ad esempio, tutte queste opzioni sono valide:
index.cs
123456789101112131415161718// Using 3rd base constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } // Using 2nd base constructor public Dog(int age, string breed) : base(age) { this.breed = breed; } // Using 1st base constructor // If the base constructor has no arguments then it is automatically called (similar to the default constructor), so we don't necessarily need to write 'base()' public Dog(string breed) { this.breed = breed; }
Mettiamo insieme i due frammenti sopra e aggiungiamo alcune istruzioni Console.WriteLine
per vedere in quale ordine vengono eseguiti i costruttori e osservare praticamente i risultati:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394using System; class Mammal { int age; float weight; // kg // 1st Constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor // The attributes are initialized with zero values Console.WriteLine("Mammal - Constructor 1 Called"); } // 2nd Constructor public Mammal(int age) { this.age = age; Console.WriteLine("Mammal - Constructor 2 Called"); } // 3rd Constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; Console.WriteLine("Mammal - Constructor 3 Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog - Constructor 1 Called"); } // Using 1st Mammal constructor // We don't necessarily need to write 'base()' in this case // It automatically finds and calls the constructor with no arguments public Dog(string breed) { this.breed = breed; Console.WriteLine("Dog - Constructor 2 Called"); } // Using 2nd Mammal constructor public Dog(int age, string breed) : base(age) { this.breed = breed; Console.WriteLine("Dog - Constructor 3 Called"); } // Using 3rd Mammal constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; Console.WriteLine("Dog - Constructor 4 Called"); } public void speak() { Console.WriteLine("Woof!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object using different constructors Mammal m1 = new Mammal(10, 42.0f); Mammal m2 = new Mammal(10); Mammal m3 = new Mammal(); Console.WriteLine("----------"); // Seperator, for ease of reading output // Creating a "Dog" object using different constructors Dog d1 = new Dog(10, 42.0f, "Dobermann"); Console.WriteLine(""); Dog d2 = new Dog(10, "Dobermann"); Console.WriteLine(""); Dog d3 = new Dog("Dobermann"); Console.WriteLine(""); Dog d4 = new Dog(); } }
Ora che conosci le diverse caratteristiche dell'ereditarietà, è importante sapere anche come e quando utilizzarle correttamente. Di seguito sono riportati alcuni aspetti da considerare quando si valuta una struttura di classi basata sull'ereditarietà:
Equilibrio tra semplicità e flessibilità: Il sovraccarico dei costruttori consente di avere diversi costruttori che accettano tipi di argomenti differenti, ma un uso eccessivo può rendere il codice più complesso e difficile da mantenere. È buona pratica mantenere il codice della classe breve, conciso e pratico. Evitare di creare troppi costruttori per una classe per mantenere un equilibrio tra semplicità e flessibilità.
Mantenere i costruttori semplici: I costruttori dovrebbero principalmente occuparsi di inizializzare un oggetto con dati di base. È buona pratica evitare elaborazioni non necessarie e logiche complesse all'interno di un costruttore. Se sono necessari calcoli o logiche, è preferibile creare un metodo separato.
;Cattiva pratica:;
index.cs
123456789101112131415161718192021222324252627class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; if (accountType == "Savings") { // Plus 1 Percent this.balance = balance + balance * 0.01; } else if (accountType == "HighYieldSavings") { // Plus 5 percent this.balance = balance + balance * 0.05; } else { this.balance = balance; } } }
Buona Pratica:
index.cs
123456789101112131415161718192021222324252627282930class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; this.balance = balance; monthlyInterest(); } // This method might be used in other places too private void monthlyInterest() { if(accountType == "Savings") { // Plus 1 Percent balance += balance * 0.01; } else if(accountType == "HighYieldSavings") { // Plus 5 percent balance += balance * 0.05; } } }
Inizializzazione degli attributi importanti: è necessario inizializzare tutti gli attributi importanti di un oggetto con valori corretti per garantirne il corretto funzionamento, anche se si tratta di un costruttore senza argomenti.
Cattiva Pratica:
index.cs
123456789101112131415public class Car { private string brand; private string model; private int year; private double price; // Constructor does not initialize important attributes // It is also generally not a good idea to have constructors without any arguments if they're not needed. public Car() { // No initialization of attributes Console.WriteLine("Car Created"); } }
Buona pratica:
index.cs
123456789101112131415161718192021222324252627282930313233343536373839public class Car { private string brand; private string model; private int year; private double price; // Good: Constructor initializes important attributes // It also checks if the values are correct // In this case the if-else statements are not unnecessary since they are important for ensuring that the object functions correctly. public Car(string brand, string model, int year, double price) { this.brand = brand; this.model = model; // Validate and set the year // The first public car was created in 1886 :) if (year > 1886) { this.year = year; } else { Console.WriteLine("Invalid year. Setting year to default."); this.year = DateTime.Now.Year; // Set to current year as default } // Validate and set the price if (price >= 0) { this.price = price; } else { Console.WriteLine("Invalid price. Setting price to default."); this.price = 0; // Set to a default value } } }
1. Quale caratteristica ci permette di creare più costruttori per una classe?
2. Potresti aver bisogno di utilizzare uno dei concetti delle sezioni precedenti in questo quiz. Il codice seguente presenta un errore alle righe 15 e 16. Esamina attentamente il codice e decidi quale sia una soluzione efficiente per questo errore:
Grazie per i tuoi commenti!
Chieda ad AI
Chieda ad AI
Chieda pure quello che desidera o provi una delle domande suggerite per iniziare la nostra conversazione
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
Ereditarietà
Scorri per mostrare il menu
Nella sezione precedente hai esaminato il concetto di classi derivate. Questa caratteristica di una classe di ereditare proprietà da un'altra classe è chiamata Ereditarietà.
Anche se conosci già il concetto di Ereditarietà, questa volta lo approfondirai in modo leggermente più completo per comprenderlo a fondo.
Come rapido ripasso, di seguito è riportato un esempio di Ereditarietà:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kilogram (1 kg = 2.2 pounds) } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Cat myCat = new Cat(); Dog myDog = new Dog(); myCat.speak(); myDog.speak(); } }
La direttiva #pragma warning disable "warning code"
può essere utilizzata per disabilitare determinati avvisi del compilatore. In generale, è sconsigliato disabilitare gli avvisi, poiché ignorarli può portare a comportamenti imprevisti nel programma.
Il codice sopra contiene una classe genitore chiamata Mammal
e due classi derivate chiamate Cat
e Dog
.
Si noti che nessuna delle classi ha un costruttore esplicitamente definito, il che significa che queste classi utilizzeranno un costruttore predefinito quando viene creato un oggetto.
Un costruttore predefinito viene fornito automaticamente dal linguaggio di programmazione se una classe non ha costruttori esplicitamente definiti. Un costruttore predefinito è fondamentalmente un costruttore vuoto che non contiene alcun codice, ad esempio public className() {}
è come potrebbe apparire un costruttore predefinito. Poiché non inizializza esplicitamente alcun attributo, tutti gli attributi assumono valori predefiniti - chiamati anche valori zero.
Creazione manuale di un costruttore per la classe Mammal
, che inizializza un oggetto Mammal
con alcuni dati:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
Se si tenta di compilare questo programma, verranno visualizzati alcuni errori nella console. Per comprendere tali errori è necessario prima comprendere due concetti importanti relativi ai costruttori.
Il primo è che, una volta definito esplicitamente un costruttore per una classe, tale classe non dispone più di un costruttore predefinito e quindi il costruttore definito esplicitamente diventa il costruttore principale di quella classe, che in questo caso è:
index.cs
12345public Mammal(int age, float weight) { this.age = age; this.weight = weight; }
Pertanto, durante la creazione di un nuovo oggetto, è sempre necessario fornire gli argomenti richiesti dal costruttore nell'ordine corretto:
index.cs
1234567// Incorrect ways to create 'Mammal', will show an error Mammal m1 = new Mammal(); Mammal m1 = new Mammal(10); Mammal m1 = new Mammal(42.0f); // Correct way to create 'Mammal', will execute fine. Mammal m1 = new Mammal(10, 42.0f);
In secondo luogo, anche le classi derivate possono avere costruttori; tuttavia, prima che venga chiamato il costruttore di una classe derivata, viene chiamato anche il costruttore della classe base (genitore):
index.cs
1234567891011121314151617181920212223242526272829303132333435#pragma warning disable CS0169 // To disable some unnecessary warnings, using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg // No attribute is initialized explicitly in this constructor // Hence, all attributes will take up "zero" values // It is similar to a "default" constructor except it outputs a message public Mammal() { Console.WriteLine("Mammal Constructor Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog Constructor Called"); } } class ConsoleApp { static void Main() { Dog myDog = new Dog(); } }
Quando esegui questo codice, noterai che il metodo WriteLine()
dal costruttore di 'Mammal', che rappresenta la classe genitore, viene chiamato automaticamente. Questo significa che è una regola che il costruttore della classe base (chiamato anche costruttore base) venga sempre invocato prima del costruttore della classe derivata.
Questa regola vale anche nel caso di ereditarietà multilivello:

Nel diagramma sopra, il costruttore di Kitten
chiama il costruttore di Cat
prima del proprio; tuttavia, poiché anche Cat
è una classe derivata, richiama il costruttore di Mammal
prima di sé, e Mammal
richiama il costruttore di Animal
prima del proprio. Pertanto, il primo costruttore eseguito è quello della superclasse — ovvero il costruttore della classe Animal
— e poi si procede in discesa.
Se il costruttore della classe padre non accetta argomenti, viene chiamato automaticamente dal compilatore; per questo motivo il costruttore di 'Mammal' nell'esempio è stato chiamato automaticamente. Tuttavia, esaminiamo di nuovo il codice errato:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
Nel codice sopra si ricevono due errori che indicano sostanzialmente che non sono stati chiamati manualmente i costruttori base — poiché richiedono argomenti, è necessario richiamarli manualmente. La sintassi di base per invocare manualmente il costruttore della classe padre è la seguente:
index.cs
12345678910class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }
Esempio:
index.cs
1234567891011121314151617181920212223242526272829303132using System; class ExampleParentClass { int value1; int value2; public ExampleParentClass(int value1, int value2) { this.value1 = value1; } } class ExampleDerivedClass : ExampleParentClass { int value3; // The value1 and value2 arguments are passed to the base class's contructor public ExampleDerivedClass(int value1, int value2, int value3) : base (value1, value2) { this.value3 = value3; } } class ConsoleApp { static void Main() { var testObject = new ExampleDerivedClass(5, 7, 9); } }
Utilizzando questa sintassi, è possibile passare tutti i dati necessari al costruttore di Mammal
tramite i costruttori di Cat
e Dog
per correggere l'errore riscontrato in precedenza:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public Cat(int age, float weight, string furPattern) : base(age, weight) { this.furPattern = furPattern; } public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); // Creating a "Dog" object with some data Dog d1 = new Dog(10, 42.5f, "Dobermann"); Console.WriteLine("Executed Successfully"); } }
Un'altra caratteristica importante dei costruttori è che è possibile sovraccaricare i costruttori proprio come si sovraccarica qualsiasi altro metodo. È possibile creare più costruttori con un numero variabile di argomenti:
index.cs
12345678910111213141516171819202122232425class Mammal { int age; float weight; // kg // 1st constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }
In questo caso, la classe Mammal
dispone di 3 costruttori. È quindi possibile inizializzare o creare un oggetto mammifero in 3 modi diversi e il compilatore selezionerà quale costruttore chiamare in base al numero e al tipo di argomenti:
index.cs
1234// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);
Questo significa anche che puoi chiamare uno qualsiasi dei 3 costruttori dai costruttori della classe derivata. Ad esempio, tutte queste opzioni sono valide:
index.cs
123456789101112131415161718// Using 3rd base constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } // Using 2nd base constructor public Dog(int age, string breed) : base(age) { this.breed = breed; } // Using 1st base constructor // If the base constructor has no arguments then it is automatically called (similar to the default constructor), so we don't necessarily need to write 'base()' public Dog(string breed) { this.breed = breed; }
Mettiamo insieme i due frammenti sopra e aggiungiamo alcune istruzioni Console.WriteLine
per vedere in quale ordine vengono eseguiti i costruttori e osservare praticamente i risultati:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394using System; class Mammal { int age; float weight; // kg // 1st Constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor // The attributes are initialized with zero values Console.WriteLine("Mammal - Constructor 1 Called"); } // 2nd Constructor public Mammal(int age) { this.age = age; Console.WriteLine("Mammal - Constructor 2 Called"); } // 3rd Constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; Console.WriteLine("Mammal - Constructor 3 Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog - Constructor 1 Called"); } // Using 1st Mammal constructor // We don't necessarily need to write 'base()' in this case // It automatically finds and calls the constructor with no arguments public Dog(string breed) { this.breed = breed; Console.WriteLine("Dog - Constructor 2 Called"); } // Using 2nd Mammal constructor public Dog(int age, string breed) : base(age) { this.breed = breed; Console.WriteLine("Dog - Constructor 3 Called"); } // Using 3rd Mammal constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; Console.WriteLine("Dog - Constructor 4 Called"); } public void speak() { Console.WriteLine("Woof!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object using different constructors Mammal m1 = new Mammal(10, 42.0f); Mammal m2 = new Mammal(10); Mammal m3 = new Mammal(); Console.WriteLine("----------"); // Seperator, for ease of reading output // Creating a "Dog" object using different constructors Dog d1 = new Dog(10, 42.0f, "Dobermann"); Console.WriteLine(""); Dog d2 = new Dog(10, "Dobermann"); Console.WriteLine(""); Dog d3 = new Dog("Dobermann"); Console.WriteLine(""); Dog d4 = new Dog(); } }
Ora che conosci le diverse caratteristiche dell'ereditarietà, è importante sapere anche come e quando utilizzarle correttamente. Di seguito sono riportati alcuni aspetti da considerare quando si valuta una struttura di classi basata sull'ereditarietà:
Equilibrio tra semplicità e flessibilità: Il sovraccarico dei costruttori consente di avere diversi costruttori che accettano tipi di argomenti differenti, ma un uso eccessivo può rendere il codice più complesso e difficile da mantenere. È buona pratica mantenere il codice della classe breve, conciso e pratico. Evitare di creare troppi costruttori per una classe per mantenere un equilibrio tra semplicità e flessibilità.
Mantenere i costruttori semplici: I costruttori dovrebbero principalmente occuparsi di inizializzare un oggetto con dati di base. È buona pratica evitare elaborazioni non necessarie e logiche complesse all'interno di un costruttore. Se sono necessari calcoli o logiche, è preferibile creare un metodo separato.
;Cattiva pratica:;
index.cs
123456789101112131415161718192021222324252627class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; if (accountType == "Savings") { // Plus 1 Percent this.balance = balance + balance * 0.01; } else if (accountType == "HighYieldSavings") { // Plus 5 percent this.balance = balance + balance * 0.05; } else { this.balance = balance; } } }
Buona Pratica:
index.cs
123456789101112131415161718192021222324252627282930class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; this.balance = balance; monthlyInterest(); } // This method might be used in other places too private void monthlyInterest() { if(accountType == "Savings") { // Plus 1 Percent balance += balance * 0.01; } else if(accountType == "HighYieldSavings") { // Plus 5 percent balance += balance * 0.05; } } }
Inizializzazione degli attributi importanti: è necessario inizializzare tutti gli attributi importanti di un oggetto con valori corretti per garantirne il corretto funzionamento, anche se si tratta di un costruttore senza argomenti.
Cattiva Pratica:
index.cs
123456789101112131415public class Car { private string brand; private string model; private int year; private double price; // Constructor does not initialize important attributes // It is also generally not a good idea to have constructors without any arguments if they're not needed. public Car() { // No initialization of attributes Console.WriteLine("Car Created"); } }
Buona pratica:
index.cs
123456789101112131415161718192021222324252627282930313233343536373839public class Car { private string brand; private string model; private int year; private double price; // Good: Constructor initializes important attributes // It also checks if the values are correct // In this case the if-else statements are not unnecessary since they are important for ensuring that the object functions correctly. public Car(string brand, string model, int year, double price) { this.brand = brand; this.model = model; // Validate and set the year // The first public car was created in 1886 :) if (year > 1886) { this.year = year; } else { Console.WriteLine("Invalid year. Setting year to default."); this.year = DateTime.Now.Year; // Set to current year as default } // Validate and set the price if (price >= 0) { this.price = price; } else { Console.WriteLine("Invalid price. Setting price to default."); this.price = 0; // Set to a default value } } }
1. Quale caratteristica ci permette di creare più costruttori per una classe?
2. Potresti aver bisogno di utilizzare uno dei concetti delle sezioni precedenti in questo quiz. Il codice seguente presenta un errore alle righe 15 e 16. Esamina attentamente il codice e decidi quale sia una soluzione efficiente per questo errore:
Grazie per i tuoi commenti!