Использование ключевых слов "Override" и "New" (Руководство по программированию в C#)
В C# метод в производном классе может иметь то же имя, что и метод в базовом классе. Можно задать способ взаимодействия методов, воспользовавшись ключевыми словами new и override. Модификатор override
расширяет метод virtual
базового класса, а модификатор new
скрывает доступный метод базового класса. Эта разница показана в примере в этой статье.
В консольном приложении объявите два класса — BaseClass
и DerivedClass
. Интерфейс DerivedClass
наследуется от интерфейса BaseClass
.
class BaseClass
{
public void Method1()
{
Console.WriteLine("Base - Method1");
}
}
class DerivedClass : BaseClass
{
public void Method2()
{
Console.WriteLine("Derived - Method2");
}
}
В методе Main
объявите переменные bc
, dc
и bcdc
.
Параметр
bc
имеет типBaseClass
и его значение — типBaseClass
.Параметр
dc
имеет типDerivedClass
и его значение — типDerivedClass
.Параметр
bcdc
имеет типBaseClass
и его значение — типDerivedClass
. Это переменная, на которую следует обратить внимание.
Поскольку bc
и bcdc
имеют тип BaseClass
, они могут только напрямую обращаться к методу Method1
(если не используется приведение). Переменная dc
может обращаться к Method1
и Method2
. Эти связи показаны в следующем коде.
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
}
Далее добавьте следующий метод Method2
в класс BaseClass
. Сигнатура этого метода соответствует сигнатуре метода Method2
в DerivedClass
.
public void Method2()
{
Console.WriteLine("Base - Method2");
}
Поскольку BaseClass
теперь имеет метод Method2
, можно добавить второй оператор вызова для переменных bc
и bcdc
класса BaseClass
, как показано в следующем коде.
bc.Method1();
bc.Method2();
dc.Method1();
dc.Method2();
bcdc.Method1();
bcdc.Method2();
При построении проекта видно, что добавление метода Method2
в BaseClass
вызывает предупреждение. Предупреждение говорит, что метод Method2
в DerivedClass
скрывает метод Method2
в BaseClass
. Рекомендуется использовать ключевое слово new
в определении Method2
, если требуется получить такой результат. Другой вариант — переименовать один из методов Method2
для разрешения предупреждения, но это не всегда целесообразно.
Прежде чем добавлять new
, запустите программу, чтобы увидеть выходные данные дополнительных операторов вызова. Отобразятся следующие результаты.
// Output:
// Base - Method1
// Base - Method2
// Base - Method1
// Derived - Method2
// Base - Method1
// Base - Method2
Ключевое слово new
сохраняет связи, дающие этот результат, однако подавляет предупреждение. Переменные, имеющие тип BaseClass
, продолжают обращаться к элементам класса BaseClass
, а переменная, имеющая тип DerivedClass
, продолжает обращаться к элементам класса DerivedClass
в первую очередь, а затем к элементам, унаследованным от BaseClass
.
Чтобы подавить предупреждение, добавьте модификатор new
в определение Method2
в классе DerivedClass
, как показано в следующем коде. Модификатор можно добавить перед или после public
.
public new void Method2()
{
Console.WriteLine("Derived - Method2");
}
Запустите программу еще раз, чтобы убедиться, что выходные данные не изменились. Также убедитесь, что предупреждение больше не появляется. Используя ключевое слово new
, вы утверждаете, что вам известно, что модифицируемый им член скрывается членом, который наследуется от базового класса. Дополнительные сведения о скрытии имен через наследование см. в разделе Модификатор new.
Чтобы противопоставить это поведение эффекту использования override
, добавьте в DerivedClass
следующий метод. Модификатор override
можно добавить перед или после public
.
public override void Method1()
{
Console.WriteLine("Derived - Method1");
}
Добавьте модификатор virtual
в определение Method1
в BaseClass
. Модификатор virtual
можно добавить перед или после public
.
public virtual void Method1()
{
Console.WriteLine("Base - Method1");
}
Запустите проект снова. Обратите особое внимание на последние две строки следующих выходных данных.
// Output:
// Base - Method1
// Base - Method2
// Derived - Method1
// Derived - Method2
// Derived - Method1
// Base - Method2
Использование модификатора override
позволяет bcdc
осуществлять доступ к методу Method1
, который определен в DerivedClass
. Как правило, это требуемое поведение в иерархиях наследования. Требуется, чтобы объекты со значениями, созданными из производного класса, использовали определенные в производном классе методы. Это поведение достигается с использованием override
для расширения метода базового класса.
Следующий код содержит полный пример.
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");
}
}
}
В следующем примере показано подобное поведение в другом контексте. В примере определяются три класса: базовый класс Car
и два класса, производных от него: ConvertibleCar
и Minivan
. Базовый класс содержит метод DescribeCar
. Этот метод отображает общее описание автомобиля, а затем вызывает ShowDetails
для предоставления дополнительных сведений. Каждый из трех классов определяет метод ShowDetails
. Для определения метода ShowDetails
в классе ConvertibleCar
используется модификатор new
. Для определения метода ShowDetails
в классе Minivan
используется модификатор override
.
// 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.");
}
}
В примере проверяется версия вызванного ShowDetails
. Следующий метод, TestCars1
, объявляет экземпляр каждого класса, а затем вызывает DescribeCar
на каждом экземпляре.
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
формирует следующие выходные данные. Обратите особое внимание на результаты для car2
, который, вероятно, не соответствует ожидаемым. Тип объекта — ConvertibleCar
, но DescribeCar
не получает доступа к версии ShowDetails
, определенной в классе ConvertibleCar
, так как этот метод объявлен с модификатором new
, а не override
. В результате объект ConvertibleCar
отображает то же описание, что и объект Car
. Сравните результаты для car3
, который является объектом Minivan
. В этом случае метод ShowDetails
, объявленный в классе Minivan
, переопределяет метод ShowDetails
, объявленный в классе Car
, и отображается описание микроавтобуса.
// TestCars1
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Carries seven people.
// ----------
TestCars2
создает список объектов, имеющих тип Car
. Значения объектов создаются из классовCar
, ConvertibleCar
и Minivan
. DescribeCar
вызывается для каждого элемента списка. В следующем коде показано определение 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("----------");
}
}
Выводится следующий результат. Обратите внимание, что он совпадает с выходными данными, отображаемыми TestCars1
. Метод ShowDetails
класса ConvertibleCar
не вызывается, независимо от того, является ли тип объекта ConvertibleCar
, как в TestCars1
, или Car
, как в TestCars2
. И наоборот, car3
вызывает метод ShowDetails
класса Minivan
в обоих случаях, независимо от того, какого они типа — Minivan
или Car
.
// TestCars2
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Carries seven people.
// ----------
Методы TestCars3
и TestCars4
завершают пример. Эти методы вызывают ShowDetails
напрямую: сначала из объектов, объявленных с типом ConvertibleCar
и Minivan
(TestCars3
), а затем из объектов, объявленных с типом Car
(TestCars4
). Следующий код определяет эти два метода.
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();
}
Методы производят следующий результат, который соответствует результатам из первого примера в этой теме.
// TestCars3
// ----------
// A roof that opens up.
// Carries seven people.
// TestCars4
// ----------
// Standard transportation.
// Carries seven people.
В следующем коде приведен полный проект и его результат.
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.");
}
}
}