Menu
Coddy logo textTech

Polimorfismo a compile time e runtime

Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 25 di 70.

Il polimorfismo significa «molte forme»: la capacità dello stesso codice di comportarsi diversamente a seconda del contesto. C# supporta due tipi distinti di polimorfismo e capire quando viene risolto ciascuno ti aiuta a scrivere codice più flessibile.

Il polimorfismo in fase di compilazione (chiamato anche polimorfismo statico) viene risolto dal compilatore prima che il programma venga eseguito. L'overload dei metodi è l'esempio principale: più metodi condividono lo stesso nome ma hanno parametri diversi:

public class Calculator
{
    public int Add(int a, int b) => a + b;
    public double Add(double a, double b) => a + b;
}

var calc = new Calculator();
calc.Add(5, 3);       // Il compilatore sceglie la versione int
calc.Add(5.0, 3.0);   // Il compilatore sceglie la versione double

Il compilatore esamina i tipi degli argomenti e seleziona il metodo corretto in fase di compilazione. Questa decisione viene presa prima ancora che il programma venga eseguito.

Il polimorfismo a runtime (polimorfismo dinamico) viene risolto mentre il programma è in esecuzione. Questo avviene con i metodi virtual e override: il tipo effettivo dell’oggetto determina quale metodo viene eseguito:

Animal pet = new Dog();  // Dichiarato come Animal, ma in realtà è un Dog
pet.Speak();             // Chiama Speak() di Dog durante l'esecuzione

Il compilatore non sa quale Speak() verrà eseguito: la decisione viene presa in fase di esecuzione in base al tipo effettivo dell'oggetto. È questo che rende il polimorfismo basato sull'ereditarietà così potente per creare sistemi flessibili ed estensibili.

challenge icon

Sfida

Facile

Costruiamo un sistema di conversione che dimostri entrambi i tipi di polimorfismo in azione. Creerai una classe che usa l’overload dei metodi (polimorfismo in fase di compilazione) insieme a una gerarchia di ereditarietà che usa metodi virtuali/override (polimorfismo in fase di esecuzione).

Organizzerai il codice in quattro file:

  • Converter.cs: definisci una classe Converter nello spazio dei nomi Conversion. Questa classe dimostra il polimorfismo in fase di compilazione tramite l’overload dei metodi. Crea tre metodi Convert sovraccaricati:
    • Convert(int value) restituisce "Integer: {value}"
    • Convert(double value) restituisce "Double: {value}"
    • Convert(string value) restituisce "String: {value}"
  • Shape.cs: definisci una classe base Shape nello spazio dei nomi Conversion. Questa classe ha una proprietà Name (stringa) e un costruttore che la imposta. Includi un metodo virtual chiamato Describe() che restituisce "This is a {Name}".
  • Circle.cs: definisci una classe Circle nello spazio dei nomi Conversion che eredita da Shape. Aggiungi una proprietà Radius (double). Il costruttore accetta un raggio e passa "Circle" al costruttore della classe base. Esegui l’override di Describe() affinché restituisca "This is a Circle with radius {Radius}".
  • Program.cs: nel file principale, dimostra entrambi i tipi di polimorfismo. Per prima cosa, crea un Converter e chiama tutti e tre i metodi sovraccaricati con i valori di input. Poi crea un Circle, memorizzalo in una variabile Shape e chiama Describe() per mostrare il polimorfismo in fase di esecuzione.

Riceverai quattro input:

  • Un valore intero
  • Un valore double
  • Un valore stringa
  • Il raggio di un cerchio (double)

Stampa l’output in questo formato:

Compile-time Polymorphism:
{Convert(int) result}
{Convert(double) result}
{Convert(string) result}
Runtime Polymorphism:
{Describe() result from Shape variable holding Circle}

Per esempio, se gli input sono 42, 3.14, Hello e 5.5, l’output dovrebbe essere:

Compile-time Polymorphism:
Integer: 42
Double: 3.14
String: Hello
Runtime Polymorphism:
This is a Circle with radius 5.5

Nota la differenza fondamentale: il compilatore decide quale metodo Convert chiamare in base ai tipi degli argomenti in fase di compilazione, mentre il metodo Describe() viene risolto in fase di esecuzione in base al tipo effettivo dell’oggetto, anche se è memorizzato in una variabile Shape!

Provalo tu

using System;
using Conversion;

class Program
{
    public static void Main(string[] args)
    {
        // Leggi gli input
        int intValue = Convert.ToInt32(Console.ReadLine());
        double doubleValue = Convert.ToDouble(Console.ReadLine());
        string stringValue = Console.ReadLine();
        double radius = Convert.ToDouble(Console.ReadLine());

        // TODO: Dimostra il polimorfismo a tempo di compilazione
        // Crea un oggetto Converter e chiama tutte e tre le versioni sovraccaricate del metodo Convert
        Console.WriteLine("Compile-time Polymorphism:");
        // Chiama Convert con intValue, doubleValue e stringValue

        // TODO: Dimostra il polimorfismo a runtime
        // Crea un oggetto Circle e memorizzalo in una variabile Shape
        // Poi chiama Describe() sulla variabile Shape
        Console.WriteLine("Runtime Polymorphism:");
        // Crea un oggetto Circle e chiama Describe()
    }
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore C# online