Condividi tramite


Controllo delle versioni con le parole chiave Override e New (Guida per programmatori C#)

Il linguaggio C# è progettato in modo che il controllo delle versioni tra classi di base e derivate in librerie diverse possa evolversi e mantenere la compatibilità con le versioni precedenti. Ciò significa, ad esempio, che l'introduzione di un nuovo membro in una classe base con lo stesso nome di un membro in una classe derivata è completamente supportata da C# e non comporta comportamenti imprevisti. Significa anche che una classe deve indicare in modo esplicito se un metodo deve eseguire l'override di un metodo ereditato o se un metodo è un nuovo metodo che nasconde un metodo ereditato denominato in modo analogo.

In C# le classi derivate possono contenere metodi con lo stesso nome dei metodi della classe base.

  • Se il metodo nella classe derivata non è preceduto da parole chiave new o override , il compilatore genera un avviso e il metodo si comporta come se la new parola chiave fosse presente.

  • Se il metodo nella classe derivata è preceduto dalla new parola chiave , il metodo viene definito come indipendente dal metodo nella classe base.

  • Se il metodo nella classe derivata è preceduto dalla override parola chiave , gli oggetti della classe derivata chiameranno tale metodo anziché il metodo della classe base.

  • Per applicare la override parola chiave al metodo nella classe derivata, il metodo della classe base deve essere definito virtuale.

  • Il metodo della classe base può essere chiamato dall'interno della classe derivata usando la base parola chiave .

  • Le overrideparole chiave , virtuale new possono essere applicate anche a proprietà, indicizzatori ed eventi.

Per impostazione predefinita, i metodi C# non sono virtuali. Se un metodo viene dichiarato come virtuale, qualsiasi classe che eredita il metodo può implementare la propria versione. Per rendere virtuale un metodo, il virtual modificatore viene usato nella dichiarazione del metodo della classe base. La classe derivata può quindi eseguire l'override del metodo virtuale di base usando la override parola chiave o nascondere il metodo virtuale nella classe base usando la new parola chiave . Se non viene specificata né la override parola chiave né la new parola chiave , il compilatore genererà un avviso e il metodo nella classe derivata nasconderà il metodo nella classe base.

Per dimostrare questo problema in pratica, presupporre per un momento che la Società A abbia creato una classe denominata GraphicsClass, che il programma usa. Quanto segue è GraphicsClass:

class GraphicsClass
{
    public virtual void DrawLine() { }
    public virtual void DrawPoint() { }
}

L'azienda usa questa classe e la si usa per derivare la propria classe, aggiungendo un nuovo metodo:

class YourDerivedGraphicsClass : GraphicsClass
{
    public void DrawRectangle() { }
}

L'applicazione viene usata senza problemi, fino a quando la Società A non rilascia una nuova versione di GraphicsClass, simile al codice seguente:

class GraphicsClass
{
    public virtual void DrawLine() { }
    public virtual void DrawPoint() { }
    public virtual void DrawRectangle() { }
}

La nuova versione di GraphicsClass ora contiene un metodo denominato DrawRectangle. Inizialmente, non si verifica nulla. La nuova versione è ancora compatibile a livello binario con la versione precedente. Qualsiasi software distribuito continuerà a funzionare, anche se la nuova classe è installata in tali sistemi. Qualsiasi chiamata esistente al metodo DrawRectangle continuerà a fare riferimento alla tua versione nella classe derivata.

Tuttavia, non appena si ricompila l'applicazione usando la nuova versione di GraphicsClass, si riceverà un avviso dal compilatore CS0108. Questo avviso ti informa che devi considerare come vuoi che si comporti il metodo DrawRectangle nella tua applicazione.

Per eseguire l'override del nuovo metodo della classe di base, usare la override parola chiave :

class YourDerivedGraphicsClass : GraphicsClass
{
    public override void DrawRectangle() { }
}

La override parola chiave assicura che tutti gli oggetti derivati da YourDerivedGraphicsClass useranno la versione della classe derivata di DrawRectangle. Gli oggetti derivati da YourDerivedGraphicsClass possono comunque accedere alla versione della classe base di DrawRectangle usando la parola chiave base:

base.DrawRectangle();

Se non vuoi che il tuo metodo sostituisca il metodo della classe base, si applicano le seguenti considerazioni. Per evitare confusione tra i due metodi, è possibile rinominare il metodo. Questo può richiedere molto tempo e soggetto a errori e non solo pratico in alcuni casi. Tuttavia, se il progetto è relativamente piccolo, è possibile usare le opzioni di refactoring di Visual Studio per rinominare il metodo. Per altre informazioni, vedere Refactoring di classi e tipi (Progettazione classi).

In alternativa, è possibile impedire l'avviso usando la parola chiave new nella definizione della classe derivata:

class YourDerivedGraphicsClass : GraphicsClass
{
    public new void DrawRectangle() { }
}

L'uso della new parola chiave indica al compilatore che la definizione nasconde la definizione contenuta nella classe di base. Si tratta del comportamento predefinito.

Eseguire l'override e la selezione del metodo

Quando un metodo viene denominato su una classe, il compilatore C# seleziona il metodo migliore da chiamare se più di un metodo è compatibile con la chiamata, ad esempio quando sono presenti due metodi con lo stesso nome e parametri compatibili con il parametro passato. I metodi seguenti sono compatibili:

public class Derived : Base
{
    public override void DoWork(int param) { }
    public void DoWork(double param) { }
}

Quando DoWork viene chiamato su un'istanza di Derived, il compilatore C# tenterà innanzitutto di rendere la chiamata compatibile con le versioni di DoWork dichiarate originariamente in Derived. I metodi di override non vengono considerati dichiarati in una classe, ma sono nuove implementazioni di un metodo dichiarato in una classe base. Solo se il compilatore C# non può corrispondere alla chiamata al metodo a un metodo originale in Derived, tenterà di associare la chiamata a un metodo sottoposto a override con lo stesso nome e parametri compatibili. Per esempio:

int val = 5;
Derived d = new();
d.DoWork(val);  // Calls DoWork(double).

Poiché la variabile val può essere convertita in modo implicito a un double, il compilatore C# chiama DoWork(double) anziché DoWork(int). Esistono due modi per evitare questo problema. In primo luogo, evitare di dichiarare nuovi metodi con lo stesso nome dei metodi virtuali. In secondo luogo, è possibile indicare al compilatore C# di chiamare il metodo virtuale eseguendo la ricerca nell'elenco dei metodi della classe di base eseguendo il cast dell'istanza di Derived a Base. Poiché il metodo è virtuale, verrà chiamata l'implementazione di DoWork(int) in Derived . Per esempio:

((Base)d).DoWork(val);  // Calls DoWork(int) on Derived.

Per altri esempi di new e override, vedere Sapere quando usare override e nuove parole chiave.

Vedere anche