Sapere quando utilizzare le parole chiave Override e New (Guida per programmatori C#)
Nel linguaggio C# un metodo in una classe derivata può avere lo stesso nome di un metodo in una classe di base. È possibile specificare in che modo avviene l'interazione tra i metodi usando le parole chiave new e override. Il modificatore override
estende il metodo virtual
della classe di base e il modificatore new
nasconde il metodo della classe di base accessibile. La differenza è illustrata negli esempi riportati in questo argomento.
In un'applicazione console, dichiarare le 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");
}
}
Nel metodo Main
dichiarare le variabili bc
, dc
e bcdc
.
bc
è di tipoBaseClass
e il suo valore è di tipoBaseClass
.dc
è di tipoDerivedClass
e il suo valore è di tipoDerivedClass
.bcdc
è di tipoBaseClass
e il suo valore è di tipoDerivedClass
. Si tratta della variabile a cui prestare attenzione.
Poiché bc
e bcdc
sono di tipo BaseClass
, possono solo accedere direttamente a Method1
, a meno che non venga usato il cast. Attraverso la variabile dc
è possibile accedere sia a Method1
che a 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
}
Quindi aggiungere il metodo Method2
seguente a BaseClass
. La firma di questo metodo corrisponde alla firma del metodo Method2
in DerivedClass
.
public void Method2()
{
Console.WriteLine("Base - Method2");
}
Poiché BaseClass
ha ora un metodo Method2
, è possibile aggiungere una seconda istruzione di chiamata per le variabili di BaseClass
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, l'aggiunta del metodo Method2
in BaseClass
genera un avviso. L'avviso indica che il metodo Method2
in DerivedClass
nasconde il metodo Method2
in BaseClass
. Se si intende ottenere tale risultato, è consigliabile l'uso della parola chiave new
nella definizione di Method2
. In alternativa, per risolvere il problema è possibile rinominare uno dei metodi Method2
, ma ciò non è sempre pratico.
Prima di aggiungere new
, eseguire il programma per verificare l'output prodotto da altre istruzioni di chiamata. Vengono visualizzati i risultati seguenti.
// Output:
// Base - Method1
// Base - Method2
// Base - Method1
// Derived - Method2
// Base - Method1
// Base - Method2
La parola chiave new
mantiene le relazioni che producono l'output, ma elimina l'avviso. Le variabili di tipo BaseClass
continuano ad accedere ai membri di BaseClass
, mentre la variabile di tipo DerivedClass
continua ad accedere prima ai membri in DerivedClass
e in seguito prende in considerazione i membri ereditati da BaseClass
.
Per eliminare l'avviso, aggiungere il modificatore new
alla definizione di Method2
in DerivedClass
, come illustrato nel codice seguente. Il modificatore può essere aggiunto prima o dopo l'oggetto public
.
public new void Method2()
{
Console.WriteLine("Derived - Method2");
}
Eseguire nuovamente il programma per verificare che l'output non sia stato modificato. Verificare anche che l'avviso non venga più visualizzato. Usando new
si afferma di essere informati che il membro di cui si sta eseguendo la modifica nasconde un membro ereditato dalla classe di base. Per altre informazioni su come viene nascosto un nome tramite ereditarietà, vedere Modificatore new.
Per contrastare questo comportamento agli effetti dell'uso di override
, aggiungere il metodo seguente a DerivedClass
. Il modificatore override
può essere aggiunto prima o dopo l'oggetto public
.
public override void Method1()
{
Console.WriteLine("Derived - Method1");
}
Aggiungere il modificatore virtual
alla definizione di Method1
in BaseClass
. Il modificatore virtual
può essere aggiunto prima o dopo l'oggetto public
.
public virtual void Method1()
{
Console.WriteLine("Base - Method1");
}
Eseguire di nuovo il progetto. Si notino soprattutto le ultime due linee dell'output riportato di seguito.
// Output:
// Base - Method1
// Base - Method2
// Derived - Method1
// Derived - Method2
// Derived - Method1
// Base - Method2
L'uso del modificatore override
consente a bcdc
di accedere al metodo Method1
definito in DerivedClass
. In genere si tratta del comportamento previsto nelle gerarchie di ereditarietà. Si vuole che gli oggetti dotati di valori creati dalla classe derivata usino i metodi definiti nella classe derivata stessa. Si ottiene questo comportamento usando override
per estendere il metodo della classe di 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");
}
}
}
L'esempio riportato di seguito illustra un comportamento simile in un contesto diverso. L'esempio definisce tre classi: una classe di base denominata Car
e due classi derivate da essa, ConvertibleCar
e Minivan
. La classe di base contiene un metodo DescribeCar
. Il metodo visualizza una descrizione di base di un'automobile, quindi chiama l'oggetto ShowDetails
per fornire informazioni aggiuntive. Ognuna delle tre classi definisce un metodo ShowDetails
. Il modificatore new
viene usato per definire ShowDetails
nella classe ConvertibleCar
. Il modificatore override
viene usato per definire ShowDetails
nella classe Minivan
.
// 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 verifica quale versione di ShowDetails
viene chiamata. Il metodo seguente, TestCars1
, dichiara un'istanza di ogni classe e quindi chiama DescribeCar
per 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 notino in particolare i risultati per car2
, che probabilmente non sono quelli previsti. Il tipo dell'oggetto è ConvertibleCar
, ma DescribeCar
non accede alla versione di ShowDetails
definita nella classe ConvertibleCar
perché tale metodo è dichiarato con il modificatore new
, non con il modificatore override
. Di conseguenza, un oggetto ConvertibleCar
visualizza la stessa descrizione di un oggetto Car
. Confrontare i risultati per car3
, che è un oggetto Minivan
. In questo caso, il metodo ShowDetails
che viene dichiarato nella classe Minivan
esegue l'override del metodo ShowDetails
che viene dichiarato nella classe Car
e la descrizione visualizzata descrive un furgoncino.
// TestCars1
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Carries seven people.
// ----------
TestCars2
determina la creazione di oggetti di tipo Car
. Le istanze dei valori degli oggetti vengono create dalle classi Car
, ConvertibleCar
e Minivan
. DescribeCar
viene chiamato 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("----------");
}
}
Verrà visualizzato l'output seguente. Si noti che corrisponde all'output visualizzato da TestCars1
. Il metodo ShowDetails
della classe ConvertibleCar
non viene chiamato, indipendentemente dal fatto che il tipo di oggetto sia ConvertibleCar
, come in TestCars1
o Car
come in TestCars2
. Al contrario, car3
chiama il metodo ShowDetails
della classe Minivan
in entrambi i casi, sia se è di tipo Minivan
che se è di 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 direttamente ShowDetails
, prima da oggetti dichiarati con tipo ConvertibleCar
e Minivan
(TestCars3
) e quindi da oggetti dichiarati con tipo Car
(TestCars4
). Il codice seguente definisce i 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 di questo argomento.
// TestCars3
// ----------
// A roof that opens up.
// Carries seven people.
// TestCars4
// ----------
// Standard transportation.
// Carries seven people.
Il codice seguente illustra l'esempio completo.
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.");
}
}
}