Wann müssen die Schlüsselwörter "override" und "new" verwendet werden? (C#-Programmierhandbuch)
In C# kann eine Methode in einer abgeleiteten Klasse den gleichen Namen wie eine Methode in einer Basisklasse haben. Sie können mit den Schlüsselwörtern new und override festlegen, wie die Methoden interagieren. Der override
-Modifizierer erweitert die virtual
-Methode der Basisklasse, und der new
-Modifizierer verbirgt eine zugängliche Methode der Basisklasse. Der Unterschied wird in den Beispielen in diesem Thema veranschaulicht.
Deklarieren Sie in einer Konsolenanwendung die folgenden beiden Klassen: BaseClass
und DerivedClass
. DerivedClass
erbt von BaseClass
.
class BaseClass
{
public void Method1()
{
Console.WriteLine("Base - Method1");
}
}
class DerivedClass : BaseClass
{
public void Method2()
{
Console.WriteLine("Derived - Method2");
}
}
Deklarieren Sie in der Main
-Methode die Variablen bc
, dc
und bcdc
.
bc
ist vom TypBaseClass
, und sein Wert ist vom TypBaseClass
.dc
ist vom TypDerivedClass
, und sein Wert ist vom TypDerivedClass
.bcdc
ist vom TypBaseClass
, und sein Wert ist vom TypDerivedClass
. Auf diese Variable müssen Sie achten.
Da bc
und bcdc
vom Typ BaseClass
sind, können sie nur direkt auf Method1
zugreifen, es sei denn, sie verwenden Umwandlungen. Die Variable dc
kann sowohl auf Method1
als auch auf Method2
zugreifen. Diese Beziehungen werden im folgenden Code gezeigt.
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
}
Fügen Sie als Nächstes die folgende Method2
-Methode in BaseClass
hinzu. Die Signatur dieser Methode entspricht der Signatur der Method2
-Methode in DerivedClass
.
public void Method2()
{
Console.WriteLine("Base - Method2");
}
Weil BaseClass
jetzt über eine Method2
-Methode verfügt, kann eine zweite aufrufende Anweisung für die BaseClass
-Variablen bc
und bcdc
hinzugefügt werden, wie in folgendem Code gezeigt.
bc.Method1();
bc.Method2();
dc.Method1();
dc.Method2();
bcdc.Method1();
bcdc.Method2();
Wenn Sie das Projekt erstellen, stellen sie fest, dass das Hinzufügen der Method2
-Methode in BaseClass
zu einer Warnmeldung führt. In der Warnmeldung steht, dass die Method2
-Methode in DerivedClass
die Method2
-Methode in BaseClass
verbirgt. Es wird empfohlen, dass Sie das new
-Schlüsselwort in der Method2
-Definition verwenden, wenn Sie möchten, dass es zu diesem Ergebnis kommt. Alternativ können Sie eine der Method2
-Methoden umbenennen, um die Warnung aufzulösen. Dies muss jedoch nicht immer praktikabel sein.
Führen Sie das Programm aus, bevor Sie new
hinzufügen, um die Ausgabe der zusätzlichen aufrufenden Anweisungen zu überprüfen. Die folgenden Ergebnisse werden angezeigt.
// Output:
// Base - Method1
// Base - Method2
// Base - Method1
// Derived - Method2
// Base - Method1
// Base - Method2
Das new
-Schlüsselwort erhält die Beziehungen, die diese Ausgabe verursachen, aber es unterdrückt die Warnmeldung. Die Variablen vom Typ BaseClass
greifen weiterhin auf die Member von BaseClass
zu, und die Variable des Typs DerivedClass
greift weiterhin zuerst auf Member in DerivedClass
zu, und kümmert sich dann um von BaseClass
geerbte Member.
Fügen sie den new
-Modifizierer in die Definition von Method2
in DerivedClass
ein, wie in folgendem Code gezeigt, um die Warnmeldung zu unterdrücken. Der Modifizierer kann vor oder hinter public
eingefügt werden.
public new void Method2()
{
Console.WriteLine("Derived - Method2");
}
Führen Sie das Programm erneut aus, um sicherzustellen, dass die Ausgabe gleich geblieben ist. Überprüfen Sie außerdem, ob die Warnung weiterhin angezeigt wird. Wenn Sie new
verwenden, bestätigen Sie, dass Ihnen bewusst ist, dass der davon modifizierte Member einen Member verbirgt, der von der Basisklasse vererbt wird. Weitere Informationen zum Verbergen von Namen durch die Vererbung finden Sie unter new-Modifizierer.
Fügen Sie die folgende Methode in DerivedClass
ein, um dieses Verhalten den Auswirkungen durch das Verwenden von override
gegenüberzustellen. Der override
-Modifizierer kann vor oder hinter public
eingefügt werden.
public override void Method1()
{
Console.WriteLine("Derived - Method1");
}
Fügen Sie der Definition von Method1
in BaseClass
den virtual
-Modifizierer hinzu. Der virtual
-Modifizierer kann vor oder hinter public
eingefügt werden.
public virtual void Method1()
{
Console.WriteLine("Base - Method1");
}
Führen Sie das Projekt erneut aus. Achten Sie besonders auf die letzten beiden Zeilen der folgenden Ausgabe.
// Output:
// Base - Method1
// Base - Method2
// Derived - Method1
// Derived - Method2
// Derived - Method1
// Base - Method2
Durch den override
-Modifizierer kann bcdc
auf die in DerivedClass
definierte Method1
-Methode zugreifen. Normalerweise ist dies das gewünschte Verhalten in Vererbungshierarchien. Objekte sollten Werte aufweisen, die aus der abgeleiteten Klasse erzeugt werden, um die Methoden, die in der abgeleiteten Klasse definiert sind, verwenden zu können. Dieses Verhalten erzielen Sie, indem Sie override
verwenden, um die Methode der Basisklasse zu erweitern.
Der folgende Code umfasst das vollständige Beispiel.
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");
}
}
}
Das folgende Beispiel veranschaulicht ähnliches Verhalten in unterschiedlichen Kontexten. Im Beispiel werden drei Klassen definiert: eine Basisklasse mit dem Namen Car
und zwei Klassen, die von dieser Basisklasse abgeleitet werden, ConvertibleCar
und Minivan
. Die Basisklasse enthält eine DescribeCar
-Methode. Die Methode zeigt eine grundlegende Beschreibung eines Autos und ruft dann ShowDetails
auf, um zusätzliche Informationen bereitzustellen. Jede der drei Klassen definiert eine ShowDetails
-Methode. Der new
-Modifizierer wird für die Definition von ShowDetails
in der ConvertibleCar
-Klasse verwendet. Der override
-Modifizierer wird für die Definition von ShowDetails
in der Minivan
-Klasse verwendet.
// 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.");
}
}
In dem Beispiel wird geprüft, welche Version von ShowDetails
aufgerufen wird. Die folgende Methode, TestCars1
, deklariert eine Instanz jeder Klasse und ruft dann DescribeCar
auf jeder Instanz auf.
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
erzeugt die folgende Ausgabe. Achten Sie besonders auf die Ergebnis für car2
, die wahrscheinlich nicht dem entsprechen, was Sie erwartet haben. Der Typ des Objekts ist ConvertibleCar
. Allerdings greift DescribeCar
nicht auf die Version von ShowDetails
zu, die in der ConvertibleCar
-Klasse definiert wurde, da diese Methode mit dem new
-Modifizierer und nicht dem override
-Modifizierer deklariert wird. Deshalb zeigt ein ConvertibleCar
-Objekt die gleiche Beschreibung wie ein Car
-Objekt an. Stellen Sie die Ergebnis von car3
gegenüber, das ein Minivan
-Objekt ist. In diesem Fall setzt die ShowDetails
-Methode, die in der Minivan
-Klasse deklariert wurde, die ShowDetails
-Methode, die in der Car
-Klasse deklariert wurde, außer Kraft, und es wird eine Beschreibung eines Minivans angezeigt.
// TestCars1
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Carries seven people.
// ----------
TestCars2
erstellt eine Liste von Objekten des Typs Car
. Die Werte der Objekte werden von den Klassen Car
, ConvertibleCar
und Minivan
instanziiert. DescribeCar
wird auf jedem Element in der Liste aufgerufen. Der folgende Code veranschaulicht die Definition von 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("----------");
}
}
Die folgende Ausgabe wird angezeigt. Beachten Sie, dass sie der Ausgabe, die von TestCars1
angezeigt wird, entspricht. Die ShowDetails
-Methode der ConvertibleCar
-Klasse wird nicht aufgerufen, unabhängig davon, ob der Typ des Objekts ConvertibleCar
, wie in TestCars1
, oder Car
, wie in TestCars2
, ist. Umgekehrt ruft car3
in beiden Fällen die ShowDetails
-Methode der Minivan
-Klasse auf, egal, ob es vom Typ Minivan
oder Car
ist.
// TestCars2
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Carries seven people.
// ----------
Die Methoden TestCars3
und TestCars4
schließen das Beispiel ab. Diese Methoden rufen ShowDetails
direkt auf. Zuerst von Objekte, die mit den Typen ConvertibleCar
und Minivan
(TestCars3
) deklariert wurden, und anschließend von Objekten, die mit dem Typ Car
(TestCars4
) deklariert wurden. Der folgende Code definiert diese beiden Methoden.
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();
}
Die Methoden erzeugen folgende Ausgabe, die den Ergebnissen des ersten Beispiels in diesem Thema entspricht.
// TestCars3
// ----------
// A roof that opens up.
// Carries seven people.
// TestCars4
// ----------
// Standard transportation.
// Carries seven people.
Der folgende Code veranschaulicht das vollständige Projekt und dessen Ausgabe.
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.");
}
}
}