Eseguire l'override di proprietà e metodi in una classe derivata

Completato

In C# è possibile usare la parola chiave override per estendere o modificare il comportamento della classe di base nella classe derivata. La parola chiave override consente di eseguire l'override di proprietà e metodi ereditati dalla classe di base e di fornire implementazioni personalizzate nella classe derivata. In questo modo è possibile riutilizzare il codice definito nella classe di base ed estendere o modificare il comportamento della classe di base nella classe derivata.

Per eseguire l'override di una proprietà o di un metodo in una classe derivata, è necessario seguire questa procedura:

  1. Dichiarare i membri nella classe base come abstract o virtual.
  2. Eseguire l'override dei membri nella classe derivata.
  3. Facoltativamente, usare la parola chiave base per accedere all'implementazione della classe di base dal membro sottoposto a override nella classe derivata.

Dichiarare membri astratti e virtuali della classe base

Prima di poter eseguire l'override dei membri in una classe derivata, è necessario dichiarare i membri nella classe base come abstract o virtual.

  • Abstract: la parola chiave abstract indica che il membro non ha implementazione e deve essere sottoposto a override in una classe derivata.
  • Virtual: la parola chiave virtual indica che il membro ha un'implementazione, ma può essere sottoposto a override o esteso in una classe derivata.

Nell'esempio seguente viene illustrato come dichiarare membri astratti e virtuali in una classe base:


public class BaseClass
{
    public string Property1 { get; set; } = "Base - Property1";
    public string Property2 { get; set; } = "Base - Property2";

    public virtual void Method1()
    {
        Console.WriteLine("Base - Method1");
    }

    public void Method2()
    {
        Console.WriteLine("Base - Method2");
    }
}

Eseguire l'override dei membri nella classe derivata

Dopo aver dichiarato i membri nella classe base come abstract o virtual, è possibile eseguire l'override dei membri nella classe derivata usando la parola chiave override. La parola chiave override indica che il membro nella classe derivata esegue l'override del membro nella classe base.

Si consideri l'esempio seguente:


// Step 1: Create instances of the base class and the derived classes
BaseClass baseClass = new BaseClass();
DerivedClass derivedClass = new DerivedClass();
BaseClass baseClassReferencingDerivedClass = new DerivedClass();

// Step 2: Access properties and methods of the base class
Console.WriteLine($"\n{baseClass.Property1}");
Console.WriteLine($"{baseClass.Property2}");
baseClass.Method1();
baseClass.Method2();

// Step 3: Access properties and methods of the derived class
Console.WriteLine($"\n{derivedClass.Property1}");
Console.WriteLine($"{derivedClass.Property2}");
derivedClass.Method1();
derivedClass.Method2();

// Step 4: Access properties and methods of the base class that references the derived class
Console.WriteLine($"\n{baseClassReferencingDerivedClass.Property1}");
Console.WriteLine($"{baseClassReferencingDerivedClass.Property2}");
baseClassReferencingDerivedClass.Method1();
baseClassReferencingDerivedClass.Method2();

/*Output:
Base - Property1
Base - Property2
Base - Method1
Base - Method2

Derived - Property1
Derived - Property2
Derived - Method1
Derived - Method2

Derived - Property1
Base - Property2
Derived - Method1
Base - Method2
*/

public class BaseClass
{
    public virtual string Property1 { get; set; } = "Base - Property1";
    public virtual string Property2 { get; set; } = "Base - Property2";

    public virtual void Method1()
    {
        Console.WriteLine("Base - Method1");
    }

    public void Method2()
    {
        Console.WriteLine("Base - Method2");
    }
}

public class DerivedClass : BaseClass
{
    public override string Property1 { get; set; } = "Derived - Property1";
    public new string Property2 { get; set; } = "Derived - Property2";

    public override void Method1()
    {
        Console.WriteLine("Derived - Method1");
    }

    public new void Method2()
    {
        Console.WriteLine("Derived - Method2");
    }
}

Questo codice illustra come modificare e nascondere proprietà e metodi in una classe derivata usando le parole chiave override e new. Il codice definisce una classe base (BaseClass) e una classe derivata (DerivedClass) che eredita dalla classe base. La classe base ha due proprietà (Property1 e Property2) e due metodi (Method1 e Method2). La classe derivata esegue l'override di Property1 e Method1 e nasconde Property2 e Method2 usando la parola chiave new.

Ecco una suddivisione del codice:

  1. Nel passaggio 1, il codice crea un'istanza della classe base (baseClass), un'istanza della classe derivata (derivedClass) e un'istanza della classe derivata a cui fa riferimento una variabile di classe base (baseClassReferencingDerivedClass). L'istanza di baseClassReferencingDerivedClass illustra il polimorfismo, in cui un riferimento di classe di base punta a un oggetto classe derivata.

  2. Nel passaggio 2, il codice accede e stampa le proprietà e i metodi dell'istanza della classe di base. Poiché baseClass è un'istanza di BaseClass, usa le proprietà e i metodi definiti in BaseClass. L'output mostra i valori e i comportamenti definiti nella classe base.

  3. Nel passaggio 3, il codice accede e stampa le proprietà e i metodi dell'istanza della classe derivata. Poiché derivedClass è un'istanza di DerivedClass, usa le proprietà e i metodi sottoposti a override definiti in DerivedClass. L'output riflette i valori e i comportamenti definiti nella classe derivata.

  4. Nel passaggio 4, il codice accede e stampa le proprietà e i metodi dell'istanza della classe derivata a cui fa riferimento una variabile di classe di base. Per le proprietà e i metodi sottoposti a override (Property1 e Method1), viene usata l'implementazione della classe derivata. Tuttavia, per proprietà e metodi nascosti (Property2 e Method2), viene usata l'implementazione della classe di base. In questo modo viene illustrata la differenza tra l'override del metodo (usando override) e il metodo nascosto (usando new).

Definizione di una classe base astratta

Quando si definisce una classe base come abstract, si indica che la classe è incompleta e deve essere implementata dalle classi derivate. Le classi astratte possono contenere proprietà astratte e metodi che devono essere implementati dalle classi derivate. Le classi di base astratte non possono essere create direttamente, quindi vengono usate come modello per le classi derivate per implementare i membri astratti.

Si consideri l'esempio seguente:


// create instances of the base class and the derived classes
// Note: We can't create an instance of an abstract class
// BaseClass baseClass = new BaseClass(); // This line would cause a compile-time error
DerivedClass derivedClass = new DerivedClass();
BaseClass baseClassReferencingDerivedClass = new DerivedClass();

// access properties and methods of the derived class
Console.WriteLine($"\n{derivedClass.Property1}");
Console.WriteLine($"{derivedClass.Property2}");
derivedClass.Method1();
derivedClass.Method2();

// access properties and methods of the base class that references the derived class
Console.WriteLine($"\n{baseClassReferencingDerivedClass.Property1}");
Console.WriteLine($"{baseClassReferencingDerivedClass.Property2}");
baseClassReferencingDerivedClass.Method1();
baseClassReferencingDerivedClass.Method2();

/*Output:
Derived - Property1
Derived - Property2
Derived - Method1
Derived - Method2

Derived - Property1
Base - Property2
Derived - Method1
Base - Method2
*/

public abstract class BaseClass
{
    public abstract string Property1 { get; set; }
    public virtual string Property2 { get; set; } = "Base - Property2";

    public abstract void Method1();

    public void Method2()
    {
        Console.WriteLine("Base - Method2");
    }
}

public class DerivedClass : BaseClass
{
    public override string Property1 { get; set; } = "Derived - Property1";
    public new string Property2 { get; set; } = "Derived - Property2";

    public override void Method1()
    {
        Console.WriteLine("Derived - Method1");
    }

    public new void Method2()
    {
        Console.WriteLine("Derived - Method2");
    }
}

Si noti che quando la classe base viene dichiarata usando la parola chiave abstract, non è possibile creare direttamente un'istanza della classe base. È invece necessario creare istanze delle classi derivate che implementano i membri astratti. La classe derivata deve fornire implementazioni per i membri astratti definiti nella classe base. I membri che non sono astratti possono essere sottoposti a override o nascosti nella classe derivata.

Eseguire l'override di membri ereditati in modo implicito della classe Object

Tutte le classi in C# ereditano in modo implicito dalla classe Object. La classe Object definisce diversi membri ereditati da tutte le classi, ad esempio ToString, Equalse GetHashCode. È possibile eseguire l'override di questi membri nelle classi per fornire implementazioni personalizzate.

Si consideri l'esempio seguente:


// create instances of the derived classes
DerivedClass1 derivedClass1 = new DerivedClass1();
DerivedClass2 derivedClass2a = new DerivedClass2 { Property1 = "Value1", Property2 = "Value2" };
DerivedClass2 derivedClass2b = new DerivedClass2 { Property1 = "Value1", Property2 = "Value2" };
DerivedClass2 derivedClass2c = new DerivedClass2 { Property1 = "Value3", Property2 = "Value4" };

// demonstrate Object class methods for DerivedClass1
Console.WriteLine("\nDemonstrating Object class methods for DerivedClass1:");
Console.WriteLine($"ToString: {derivedClass1.ToString()}");
Console.WriteLine($"Equals: {derivedClass1.Equals(new DerivedClass1())}");
Console.WriteLine($"GetHashCode: {derivedClass1.GetHashCode()}");

// demonstrate overridden Object class methods for DerivedClass2
Console.WriteLine("\nDemonstrating overridden Object class methods for DerivedClass2:");
Console.WriteLine($"ToString: {derivedClass2a.ToString()}");
Console.WriteLine($"Equals (derivedClass2a vs derivedClass2b): {derivedClass2a.Equals(derivedClass2b)}"); // should return true
Console.WriteLine($"Equals (derivedClass2a vs derivedClass2c): {derivedClass2a.Equals(derivedClass2c)}"); // should return false
Console.WriteLine($"GetHashCode (derivedClass2a): {derivedClass2a.GetHashCode()}");
Console.WriteLine($"GetHashCode (derivedClass2b): {derivedClass2b.GetHashCode()}");
Console.WriteLine($"GetHashCode (derivedClass2c): {derivedClass2c.GetHashCode()}");

/*Output:
Demonstrating Object class methods for DerivedClass1:
ToString: DerivedClass1
Equals: False
GetHashCode: [HashCode]

Demonstrating overridden Object class methods for DerivedClass2:
ToString: DerivedClass2: Property1 = Value1, Property2 = Value2
Equals (derivedClass2a vs derivedClass2b): True
Equals (derivedClass2a vs derivedClass2c): False
GetHashCode (derivedClass2a): [HashCode]
GetHashCode (derivedClass2b): [HashCode]
GetHashCode (derivedClass2c): [HashCode]
*/

public abstract class BaseClass
{
    public abstract string Property1 { get; set; }
    public virtual string Property2 { get; set; } = "Base - Property2";

    public abstract void Method1();

    public void Method2()
    {
        Console.WriteLine("Base - Method2");
    }
}

public class DerivedClass1 : BaseClass
{
    public override string Property1 { get; set; } = "Derived1 - Property1";
    public new string Property2 { get; set; } = "Derived1 - Property2";

    public override void Method1()
    {
        Console.WriteLine("Derived1 - Method1");
    }

    public new void Method2()
    {
        Console.WriteLine("Derived1 - Method2");
    }
}

public class DerivedClass2 : BaseClass
{
    public override string Property1 { get; set; } = "Derived2 - Property1";
    public new string Property2 { get; set; } = "Derived2 - Property2";

    public override void Method1()
    {
        Console.WriteLine("Derived2 - Method1");
    }

    public new void Method2()
    {
        Console.WriteLine("Derived2 - Method2");
    }

    // Override ToString method
    public override string ToString()
    {
        return $"DerivedClass2: Property1 = {Property1}, Property2 = {Property2}";
    }

    // Override Equals method
    public override bool Equals(object obj)
    {
        if (obj is DerivedClass2 other)
        {
            return Property1 == other.Property1 && Property2 == other.Property2;
        }
        return false;
    }

    // Override GetHashCode method
    public override int GetHashCode()
    {
        return HashCode.Combine(Property1, Property2);
    }
}

In questo esempio le classi DerivedClass1 e DerivedClass2 ereditano dalla classe BaseClass. La classe DerivedClass2 esegue l'override dei metodi ToString, Equalse GetHashCode ereditati dalla classe Object. Il metodo ToString restituisce una rappresentazione di stringa dell'oggetto, il metodo Equals confronta due oggetti per verificarne l'uguaglianza e il metodo GetHashCode restituisce un codice hash per l'oggetto. La classe DerivedClass2 fornisce implementazioni personalizzate per questi metodi per visualizzare le proprietà dell'oggetto e confrontare gli oggetti in base alle relative proprietà.

Sommario

L'override di proprietà e metodi in una classe derivata consente di estendere o modificare il comportamento della classe di base. Usando la parola chiave override, è possibile fornire implementazioni personalizzate per proprietà e metodi ereditati dalla classe base. In questo modo è possibile riutilizzare il codice definito nella classe di base ed estendere o modificare il comportamento della classe di base nella classe derivata. È anche possibile usare la parola chiave new per nascondere proprietà e metodi nella classe base e fornire nuove implementazioni nella classe derivata.