Condividi tramite


Sapere quando usare le parole chiave Override e New (Guida per programmatori C#)

In C# un metodo in una classe derivata può avere lo stesso nome di un metodo nella classe base. È possibile specificare il modo in cui i metodi interagiscono usando le parole chiave new e override . Il override modificatore estende il metodo della classe virtual base e il new modificatore nasconde un metodo di classe base accessibile. La differenza è illustrata negli esempi di questo argomento.

In un'applicazione console, dichiarare le seguenti due classi, BaseClass e DerivedClass. DerivedClass eredita da BaseClass.

class BaseClass  
{  
    public void Method1()  
    {  
        Console.WriteLine("Base - Method1");  
    }  
}  
  
class DerivedClass : BaseClass  
{  
    public void Method2()  
    {  
        Console.WriteLine("Derived - Method2");  
    }  
}  

Main Nel metodo dichiarare le variabili bc, dce bcdc.

  • bc è di tipo BaseClasse il relativo valore è di tipo BaseClass.

  • dc è di tipo DerivedClasse il relativo valore è di tipo DerivedClass.

  • bcdc è di tipo BaseClasse il relativo valore è di tipo DerivedClass. Si tratta della variabile a cui prestare attenzione.

Poiché bc e bcdc hanno tipo BaseClass, possono accedere direttamente solo a Method1, a meno che non si usi il cast. La variabile dc può accedere sia Method1 che Method2. Queste relazioni sono illustrate nel codice seguente.

class Program  
{  
    static void Main(string[] args)  
    {  
        BaseClass bc = new BaseClass();  
        DerivedClass dc = new DerivedClass();  
        BaseClass bcdc = new DerivedClass();  
  
        bc.Method1();  
        dc.Method1();  
        dc.Method2();  
        bcdc.Method1();  
    }  
    // Output:  
    // Base - Method1  
    // Base - Method1  
    // Derived - Method2  
    // Base - Method1  
}  

Aggiungere quindi il metodo seguente Method2 a BaseClass. La firma di questo metodo corrisponde alla firma del Method2 metodo in DerivedClass.

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

Poiché BaseClass ora include un Method2 metodo, è possibile aggiungere una seconda istruzione chiamante per BaseClass le variabili bc e bcdc, come illustrato nel codice seguente.

bc.Method1();  
bc.Method2();  
dc.Method1();  
dc.Method2();  
bcdc.Method1();  
bcdc.Method2();  

Quando si compila il progetto, si noterà che l'aggiunta del Method2 metodo in BaseClass genera un avviso. L'avviso indica che il Method2 metodo in DerivedClass nasconde il Method2 metodo in BaseClass. È consigliabile usare la new parola chiave nella Method2 definizione se si intende causare tale risultato. In alternativa, è possibile rinominare uno dei Method2 metodi per risolvere l'avviso, ma questo non è sempre pratico.

Prima di aggiungere new, eseguire il programma per visualizzare l'output prodotto dalle istruzioni chiamanti aggiuntive. Vengono visualizzati i risultati seguenti.

// Output:  
// Base - Method1  
// Base - Method2  
// Base - Method1  
// Derived - Method2  
// Base - Method1  
// Base - Method2  

La new parola chiave mantiene le relazioni che producono tale output, ma elimina l'avviso. Le variabili con tipo BaseClass continuano ad accedere ai membri di BaseClasse la variabile con tipo DerivedClass continua ad accedere ai membri in DerivedClass primo luogo e quindi a considerare i membri ereditati da BaseClass.

Per eliminare l'avviso, aggiungere il new modificatore alla definizione di Method2 in DerivedClass, come illustrato nel codice seguente. Il modificatore può essere aggiunto prima o dopo public.

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

Eseguire di nuovo il programma per verificare che l'output non sia stato modificato. Verificare anche che l'avviso non venga più visualizzato. Utilizzando new, stai affermando di essere consapevole che il membro che viene modificato nasconde un membro ereditato dalla classe di base. Per ulteriori informazioni su nascondere il nome tramite ereditarietà, vedere new Modifier.

Per mettere a confronto questo comportamento con gli effetti dell'utilizzo di override, aggiungere il seguente metodo a DerivedClass. Il override modificatore può essere aggiunto prima o dopo public.

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

Aggiungere il virtual modificatore alla definizione di Method1 in BaseClass. Il virtual modificatore può essere aggiunto prima o dopo public.

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

Eseguire di nuovo il progetto. Si noti in particolare le ultime due righe dell'output seguente.

// Output:  
// Base - Method1  
// Base - Method2  
// Derived - Method1  
// Derived - Method2  
// Derived - Method1  
// Base - Method2  

L'uso del override modificatore consente di bcdc accedere al Method1 metodo definito in DerivedClass. In genere, si tratta del comportamento desiderato nelle gerarchie di ereditarietà. Si desidera che gli oggetti con valori creati dalla classe derivata usino i metodi definiti nella classe derivata. Per ottenere tale comportamento, usa override per estendere il metodo della classe base.

Il codice seguente contiene l'esempio completo.

using System;  
using System.Text;  
  
namespace OverrideAndNew  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            BaseClass bc = new BaseClass();  
            DerivedClass dc = new DerivedClass();  
            BaseClass bcdc = new DerivedClass();  
  
            // The following two calls do what you would expect. They call  
            // the methods that are defined in BaseClass.  
            bc.Method1();  
            bc.Method2();  
            // Output:  
            // Base - Method1  
            // Base - Method2  
  
            // The following two calls do what you would expect. They call  
            // the methods that are defined in DerivedClass.  
            dc.Method1();  
            dc.Method2();  
            // Output:  
            // Derived - Method1  
            // Derived - Method2  
  
            // The following two calls produce different results, depending
            // on whether override (Method1) or new (Method2) is used.  
            bcdc.Method1();  
            bcdc.Method2();  
            // Output:  
            // Derived - Method1  
            // Base - Method2  
        }  
    }  
  
    class BaseClass  
    {  
        public virtual void Method1()  
        {  
            Console.WriteLine("Base - Method1");  
        }  
  
        public virtual void Method2()  
        {  
            Console.WriteLine("Base - Method2");  
        }  
    }  
  
    class DerivedClass : BaseClass  
    {  
        public override void Method1()  
        {  
            Console.WriteLine("Derived - Method1");  
        }  
  
        public new void Method2()  
        {  
            Console.WriteLine("Derived - Method2");  
        }  
    }  
}  

Nell'esempio seguente viene illustrato un comportamento simile in un contesto diverso. L'esempio definisce tre classi: una classe base denominata Car e due classi derivate da essa ConvertibleCar e Minivan. La classe base contiene un DescribeCar metodo . Il metodo visualizza una descrizione di base di un'auto e quindi chiama ShowDetails per fornire informazioni aggiuntive. Ognuna delle tre classi definisce un ShowDetails metodo. Il new modificatore viene usato per definire ShowDetails nella ConvertibleCar classe . Il override modificatore viene usato per definire ShowDetails nella Minivan classe .

// Define the base class, Car. The class defines two methods,  
// DescribeCar and ShowDetails. DescribeCar calls ShowDetails, and each derived  
// class also defines a ShowDetails method. The example tests which version of  
// ShowDetails is selected, the base class method or the derived class method.  
class Car  
{  
    public void DescribeCar()  
    {  
        System.Console.WriteLine("Four wheels and an engine.");  
        ShowDetails();  
    }  
  
    public virtual void ShowDetails()  
    {  
        System.Console.WriteLine("Standard transportation.");  
    }  
}  
  
// Define the derived classes.  
  
// Class ConvertibleCar uses the new modifier to acknowledge that ShowDetails  
// hides the base class method.  
class ConvertibleCar : Car  
{  
    public new void ShowDetails()  
    {  
        System.Console.WriteLine("A roof that opens up.");  
    }  
}  
  
// Class Minivan uses the override modifier to specify that ShowDetails  
// extends the base class method.  
class Minivan : Car  
{  
    public override void ShowDetails()  
    {  
        System.Console.WriteLine("Carries seven people.");  
    }  
}  

L'esempio testa quale versione di ShowDetails viene chiamata. Il metodo seguente, TestCars1, dichiara un'istanza di ogni classe e quindi chiama DescribeCar in ogni istanza.

public static void TestCars1()  
{  
    System.Console.WriteLine("\nTestCars1");  
    System.Console.WriteLine("----------");  
  
    Car car1 = new Car();  
    car1.DescribeCar();  
    System.Console.WriteLine("----------");  
  
    // Notice the output from this test case. The new modifier is  
    // used in the definition of ShowDetails in the ConvertibleCar  
    // class.
  
    ConvertibleCar car2 = new ConvertibleCar();  
    car2.DescribeCar();  
    System.Console.WriteLine("----------");  
  
    Minivan car3 = new Minivan();  
    car3.DescribeCar();  
    System.Console.WriteLine("----------");  
}  

TestCars1 produce l'output seguente. Si noti in particolare i risultati per car2, che probabilmente non sono quello previsto. Il tipo dell'oggetto è ConvertibleCar, ma DescribeCar non accede alla versione di ShowDetails definita nella ConvertibleCar classe perché tale metodo viene dichiarato con il new modificatore, non con il override modificatore. Di conseguenza, un ConvertibleCar oggetto visualizza la stessa descrizione di un Car oggetto . Confrontare i risultati per car3, ovvero un Minivan oggetto . In questo caso, il metodo ShowDetails dichiarato nella classe Minivan sovrascrive il metodo ShowDetails dichiarato nella classe Car, e la descrizione visualizzata descrive un minivan.

// TestCars1  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Carries seven people.  
// ----------  

TestCars2 crea un elenco di oggetti con tipo Car. I valori degli oggetti sono istanziati dalle classi Car, ConvertibleCar e Minivan. DescribeCar viene invocato per ogni elemento dell'elenco. Il codice seguente illustra la definizione di TestCars2.

public static void TestCars2()  
{  
    System.Console.WriteLine("\nTestCars2");  
    System.Console.WriteLine("----------");  
  
    var cars = new List<Car> { new Car(), new ConvertibleCar(),
        new Minivan() };  
  
    foreach (var car in cars)  
    {  
        car.DescribeCar();  
        System.Console.WriteLine("----------");  
    }  
}  

Viene visualizzato l'output seguente. Si noti che è uguale all'output visualizzato da TestCars1. Il ShowDetails metodo della ConvertibleCar classe non viene chiamato, indipendentemente dal fatto che il tipo dell'oggetto sia ConvertibleCar, come in TestCars1o Car, come in TestCars2. Viceversa, car3 chiama il ShowDetails metodo dalla Minivan classe in entrambi i casi, indipendentemente dal tipo Minivan o dal tipo Car.

// TestCars2  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Carries seven people.  
// ----------  

I metodi TestCars3 e TestCars4 completano l'esempio. Questi metodi chiamano ShowDetails direttamente, prima dagli oggetti aventi tipo ConvertibleCar e Minivan (TestCars3), quindi dagli oggetti aventi tipo Car (TestCars4). Il codice seguente definisce questi due metodi.

public static void TestCars3()  
{  
    System.Console.WriteLine("\nTestCars3");  
    System.Console.WriteLine("----------");  
    ConvertibleCar car2 = new ConvertibleCar();  
    Minivan car3 = new Minivan();  
    car2.ShowDetails();  
    car3.ShowDetails();  
}  
  
public static void TestCars4()  
{  
    System.Console.WriteLine("\nTestCars4");  
    System.Console.WriteLine("----------");  
    Car car2 = new ConvertibleCar();  
    Car car3 = new Minivan();  
    car2.ShowDetails();  
    car3.ShowDetails();  
}  

I metodi producono l'output seguente, che corrisponde ai risultati del primo esempio in questo argomento.

// TestCars3  
// ----------  
// A roof that opens up.  
// Carries seven people.  
  
// TestCars4  
// ----------  
// Standard transportation.  
// Carries seven people.  

Il codice seguente mostra il progetto completo e il relativo output.

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
  
namespace OverrideAndNew2  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            // Declare objects of the derived classes and test which version  
            // of ShowDetails is run, base or derived.  
            TestCars1();  
  
            // Declare objects of the base class, instantiated with the  
            // derived classes, and repeat the tests.  
            TestCars2();  
  
            // Declare objects of the derived classes and call ShowDetails  
            // directly.  
            TestCars3();  
  
            // Declare objects of the base class, instantiated with the  
            // derived classes, and repeat the tests.  
            TestCars4();  
        }  
  
        public static void TestCars1()  
        {  
            System.Console.WriteLine("\nTestCars1");  
            System.Console.WriteLine("----------");  
  
            Car car1 = new Car();  
            car1.DescribeCar();  
            System.Console.WriteLine("----------");  
  
            // Notice the output from this test case. The new modifier is  
            // used in the definition of ShowDetails in the ConvertibleCar  
            // class.
            ConvertibleCar car2 = new ConvertibleCar();  
            car2.DescribeCar();  
            System.Console.WriteLine("----------");  
  
            Minivan car3 = new Minivan();  
            car3.DescribeCar();  
            System.Console.WriteLine("----------");  
        }  
        // Output:  
        // TestCars1  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Carries seven people.  
        // ----------  
  
        public static void TestCars2()  
        {  
            System.Console.WriteLine("\nTestCars2");  
            System.Console.WriteLine("----------");  
  
            var cars = new List<Car> { new Car(), new ConvertibleCar(),
                new Minivan() };  
  
            foreach (var car in cars)  
            {  
                car.DescribeCar();  
                System.Console.WriteLine("----------");  
            }  
        }  
        // Output:  
        // TestCars2  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Carries seven people.  
        // ----------  
  
        public static void TestCars3()  
        {  
            System.Console.WriteLine("\nTestCars3");  
            System.Console.WriteLine("----------");  
            ConvertibleCar car2 = new ConvertibleCar();  
            Minivan car3 = new Minivan();  
            car2.ShowDetails();  
            car3.ShowDetails();  
        }  
        // Output:  
        // TestCars3  
        // ----------  
        // A roof that opens up.  
        // Carries seven people.  
  
        public static void TestCars4()  
        {  
            System.Console.WriteLine("\nTestCars4");  
            System.Console.WriteLine("----------");  
            Car car2 = new ConvertibleCar();  
            Car car3 = new Minivan();  
            car2.ShowDetails();  
            car3.ShowDetails();  
        }  
        // Output:  
        // TestCars4  
        // ----------  
        // Standard transportation.  
        // Carries seven people.  
    }  
  
    // Define the base class, Car. The class defines two virtual methods,  
    // DescribeCar and ShowDetails. DescribeCar calls ShowDetails, and each derived  
    // class also defines a ShowDetails method. The example tests which version of  
    // ShowDetails is used, the base class method or the derived class method.  
    class Car  
    {  
        public virtual void DescribeCar()  
        {  
            System.Console.WriteLine("Four wheels and an engine.");  
            ShowDetails();  
        }  
  
        public virtual void ShowDetails()  
        {  
            System.Console.WriteLine("Standard transportation.");  
        }  
    }  
  
    // Define the derived classes.  
  
    // Class ConvertibleCar uses the new modifier to acknowledge that ShowDetails  
    // hides the base class method.  
    class ConvertibleCar : Car  
    {  
        public new void ShowDetails()  
        {  
            System.Console.WriteLine("A roof that opens up.");  
        }  
    }  
  
    // Class Minivan uses the override modifier to specify that ShowDetails  
    // extends the base class method.  
    class Minivan : Car  
    {  
        public override void ShowDetails()  
        {  
            System.Console.WriteLine("Carries seven people.");  
        }  
    }  
  
}  

Vedere anche