Polimorfismo
Il termine Polimorfismo significa "presentarsi in diverse forme". Nel contesto della programmazione orientata agli oggetti, il polimorfismo si riferisce alla capacità di una classe di fornire un'interfaccia comune per le sue classi derivate, consentendo a ciascuna classe derivata di implementare comportamenti specifici per i metodi ereditati.
Normalmente, quando si crea un oggetto di una classe, si crea una variabile per contenere il riferimento a quell'oggetto. Il tipo della variabile è solitamente la classe di quell'oggetto.
Ad esempio:
index.cs
123Dog myVar = new Dog(); // or even in case of implicit declaration, 'myVar' will be of type 'Dog' var myVar = new Dog();
Tuttavia, è anche possibile creare una variabile di una classe base e memorizzare in essa un oggetto di una classe derivata.
Ad esempio:
index.cs
1234567891011121314151617181920212223242526272829303132using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Console.WriteLine(example.breed); // Error because it's not accessible (read explanation below) } }
L'attributo breed
dell'oggetto Dog non è accessibile perché, quando si memorizza l'oggetto Dog
in una variabile di tipo Mammal
, esso viene convertito implicitamente in Mammal
. Questo significa che si possono accedere solo agli attributi e ai metodi presenti nella classe Mammal
. Tuttavia, il valore memorizzato nella classe breed
non viene perso, ma semplicemente reso inaccessibile e si può dire che sia nascosto.
È possibile convertire example
in un oggetto Dog
tramite un cast esplicito di tipo e l'attributo breed
tornerà disponibile:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } public void speak() { // Empty for now } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Dog casted = (Dog)example; Console.WriteLine(casted.breed); } }
Tuttavia, per i metodi esiste una funzionalità aggiuntiva chiamata override del metodo, che è in qualche modo simile al sovraccarico del metodo. In questo caso, si ridefinisce essenzialmente un metodo di una classe base nella classe derivata.
Ad esempio, si può voler che Mammal
abbia un metodo speak()
tale che produca Woof!
se viene chiamato da un oggetto Dog
, e Meow!
se viene chiamato da un oggetto Cat
:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647using System; class Mammal { public void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // The following execute the overriden version of the methods Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); // All of these execute the 'Mammal' version of the method m1.speak(); m2.speak(); m3.speak(); } }
Gli avvisi che si ricevono durante questa compilazione sono dovuti alla sovrapposizione dei nomi dei metodi nelle classi padre e figlio. In questo caso, c'è una sovrapposizione tra i metodi speak
delle classi Cat
e Mammal
, e anche tra Dog
e Mammal
, motivo per cui si ricevono due avvisi.
Tuttavia, si noti alla fine come m1
, m2
e m3
eseguano il metodo definito nella classe Mammal
, poiché sono stati convertiti in oggetti di tipo Mammal
.
Se si desidera che il metodo speak
si comporti come nel suo oggetto originale, è sufficiente convertirlo in un metodo virtuale. Un metodo virtuale è semplicemente un metodo che consente di essere sovrascritto dalle classi derivate in modo tale che l'implementazione specifica eseguita venga determinata a runtime in base al tipo dell'oggetto originale.
Per rendere un metodo virtuale, basta aggiungere la parola chiave virtual
prima del suo tipo di ritorno:
index.cs
1234567class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } }
Per creare un'implementazione sovrascritta di questo metodo, utilizzare la parola chiave override
prima del tipo di ritorno nell'implementazione della nuova versione nella classe derivata:
index.cs
123456789101112131415class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } }
Mettiamo ora insieme i due frammenti di codice e vediamo come questo influisce sull'output:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647using System; class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); m1.speak(); // Since it stores a Mammal() object, it executes the Mammal's implementation m2.speak(); // Output: Meow! m3.speak(); // Output: Woof! // The following behave the same as they did in the previous example: Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); } }
Si nota che nell'output, m2
e m3
utilizzano l'implementazione del metodo di Cat
e Dog
perché quegli oggetti erano originariamente Cat
e Dog
.
Questo intero processo di sovrascrittura dei metodi in modo che si comportino in base al loro tipo originale è chiamato polimorfismo.
Una situazione in cui questo può essere molto utile è la possibilità di memorizzare oggetti di diversi tipi di dati in un unico array o elenco.
Ad esempio:
index.cs
123456789List<Shape> shapes = new List<Shape>(); shapes.Add(new Rectangle(25, 50)); shapes.Add(new Square(50)); shapes.Add(new Circle(10)); shapes[0].getArea(); // Output: 1250.0f shapes[1].getArea(); // Output: 2500.0f shapes[2].getArea(); // Output: 314.15f
1. Quali sono le due parole chiave utilizzate nell'override dei metodi?
2. Qual è il modo corretto per rendere un metodo sovrascrivibile?
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
Awesome!
Completion rate improved to 2.04
Polimorfismo
Scorri per mostrare il menu
Il termine Polimorfismo significa "presentarsi in diverse forme". Nel contesto della programmazione orientata agli oggetti, il polimorfismo si riferisce alla capacità di una classe di fornire un'interfaccia comune per le sue classi derivate, consentendo a ciascuna classe derivata di implementare comportamenti specifici per i metodi ereditati.
Normalmente, quando si crea un oggetto di una classe, si crea una variabile per contenere il riferimento a quell'oggetto. Il tipo della variabile è solitamente la classe di quell'oggetto.
Ad esempio:
index.cs
123Dog myVar = new Dog(); // or even in case of implicit declaration, 'myVar' will be of type 'Dog' var myVar = new Dog();
Tuttavia, è anche possibile creare una variabile di una classe base e memorizzare in essa un oggetto di una classe derivata.
Ad esempio:
index.cs
1234567891011121314151617181920212223242526272829303132using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Console.WriteLine(example.breed); // Error because it's not accessible (read explanation below) } }
L'attributo breed
dell'oggetto Dog non è accessibile perché, quando si memorizza l'oggetto Dog
in una variabile di tipo Mammal
, esso viene convertito implicitamente in Mammal
. Questo significa che si possono accedere solo agli attributi e ai metodi presenti nella classe Mammal
. Tuttavia, il valore memorizzato nella classe breed
non viene perso, ma semplicemente reso inaccessibile e si può dire che sia nascosto.
È possibile convertire example
in un oggetto Dog
tramite un cast esplicito di tipo e l'attributo breed
tornerà disponibile:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } public void speak() { // Empty for now } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Dog casted = (Dog)example; Console.WriteLine(casted.breed); } }
Tuttavia, per i metodi esiste una funzionalità aggiuntiva chiamata override del metodo, che è in qualche modo simile al sovraccarico del metodo. In questo caso, si ridefinisce essenzialmente un metodo di una classe base nella classe derivata.
Ad esempio, si può voler che Mammal
abbia un metodo speak()
tale che produca Woof!
se viene chiamato da un oggetto Dog
, e Meow!
se viene chiamato da un oggetto Cat
:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647using System; class Mammal { public void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // The following execute the overriden version of the methods Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); // All of these execute the 'Mammal' version of the method m1.speak(); m2.speak(); m3.speak(); } }
Gli avvisi che si ricevono durante questa compilazione sono dovuti alla sovrapposizione dei nomi dei metodi nelle classi padre e figlio. In questo caso, c'è una sovrapposizione tra i metodi speak
delle classi Cat
e Mammal
, e anche tra Dog
e Mammal
, motivo per cui si ricevono due avvisi.
Tuttavia, si noti alla fine come m1
, m2
e m3
eseguano il metodo definito nella classe Mammal
, poiché sono stati convertiti in oggetti di tipo Mammal
.
Se si desidera che il metodo speak
si comporti come nel suo oggetto originale, è sufficiente convertirlo in un metodo virtuale. Un metodo virtuale è semplicemente un metodo che consente di essere sovrascritto dalle classi derivate in modo tale che l'implementazione specifica eseguita venga determinata a runtime in base al tipo dell'oggetto originale.
Per rendere un metodo virtuale, basta aggiungere la parola chiave virtual
prima del suo tipo di ritorno:
index.cs
1234567class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } }
Per creare un'implementazione sovrascritta di questo metodo, utilizzare la parola chiave override
prima del tipo di ritorno nell'implementazione della nuova versione nella classe derivata:
index.cs
123456789101112131415class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } }
Mettiamo ora insieme i due frammenti di codice e vediamo come questo influisce sull'output:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647using System; class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); m1.speak(); // Since it stores a Mammal() object, it executes the Mammal's implementation m2.speak(); // Output: Meow! m3.speak(); // Output: Woof! // The following behave the same as they did in the previous example: Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); } }
Si nota che nell'output, m2
e m3
utilizzano l'implementazione del metodo di Cat
e Dog
perché quegli oggetti erano originariamente Cat
e Dog
.
Questo intero processo di sovrascrittura dei metodi in modo che si comportino in base al loro tipo originale è chiamato polimorfismo.
Una situazione in cui questo può essere molto utile è la possibilità di memorizzare oggetti di diversi tipi di dati in un unico array o elenco.
Ad esempio:
index.cs
123456789List<Shape> shapes = new List<Shape>(); shapes.Add(new Rectangle(25, 50)); shapes.Add(new Square(50)); shapes.Add(new Circle(10)); shapes[0].getArea(); // Output: 1250.0f shapes[1].getArea(); // Output: 2500.0f shapes[2].getArea(); // Output: 314.15f
1. Quali sono le due parole chiave utilizzate nell'override dei metodi?
2. Qual è il modo corretto per rendere un metodo sovrascrivibile?
Grazie per i tuoi commenti!