Поделиться через


Знание того, когда следует использовать переопределение и новые ключевые слова (руководство по программированию на C#)

В C#метод в производном классе может иметь то же имя, что и метод в базовом классе. Вы можете указать, как методы взаимодействуют с помощью новых и переопределения ключевых слов. Модификатор 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 метод, можно добавить вторую вызывающую инструкцию для переменных BaseClassbc и bcdc, как показано в следующем коде.

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 Modifier.

Чтобы сравнить это поведение с последствиями использования 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 метод. new Модификатор используется для определения ShowDetails в ConvertibleCar классе. override Модификатор используется для определения ShowDetails в 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.");  
    }  
}  

В примере проверяется, какая версия 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.");  
        }  
    }  
  
}  

См. также