Użycie przesłonięć i nowych słów kluczowych (Przewodnik programowania w języku C#)

W języku C# metoda w klasie pochodnej może mieć taką samą nazwę jak metoda w klasie bazowej. Możesz określić sposób interakcji metod przy użyciu nowych słów kluczowych i przesłaniania . override Modyfikator rozszerza metodę klasy virtual bazowej, a new modyfikator ukrywa dostępną metodę klasy bazowej. Różnica jest pokazana w przykładach w tym temacie.

W aplikacji konsolowej zadeklaruj następujące dwie klasy i BaseClassDerivedClass. DerivedClass dziedziczy z BaseClass.

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

W metodzie zadeklaruj Main zmienne bc, dci bcdc.

  • bc jest typu BaseClass, a jego wartość jest typu BaseClass.

  • dc jest typu DerivedClass, a jego wartość jest typu DerivedClass.

  • bcdc jest typu BaseClass, a jego wartość jest typu DerivedClass. Jest to zmienna, na która należy zwrócić uwagę.

Ponieważ bc i bcdc mają typ BaseClass, mogą uzyskiwać bezpośredni dostęp tylko do Method1metody , chyba że używasz rzutu. Zmienna może uzyskiwać dostęp zarówno do zmiennej dc , jak Method1 i Method2. Te relacje są wyświetlane w poniższym kodzie.

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  
}  

Następnie dodaj następującą Method2 metodę do BaseClassmetody . Podpis tej metody jest zgodny z podpisem Method2 metody w pliku DerivedClass.

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

Ponieważ BaseClass teraz ma metodę, można dodać drugą instrukcję wywołującą Method2 dla BaseClass zmiennych bc i bcdc, jak pokazano w poniższym kodzie.

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

Podczas kompilowania projektu zobaczysz, że dodanie Method2 metody w BaseClass elemecie powoduje ostrzeżenie. Ostrzeżenie informuje, że metoda w elemecie Method2 ukrywa metodę Method2 w BaseClasspliku .DerivedClass Zaleca się użycie słowa kluczowego newMethod2 w definicji, jeśli zamierzasz spowodować ten wynik. Alternatywnie można zmienić nazwę jednej z Method2 metod, aby rozwiązać to ostrzeżenie, ale nie zawsze jest to praktyczne.

Przed dodaniem newuruchom program , aby wyświetlić dane wyjściowe wygenerowane przez dodatkowe instrukcje wywołujące. Zostaną wyświetlone następujące wyniki.

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

Słowo new kluczowe zachowuje relacje, które generują te dane wyjściowe, ale pomija ostrzeżenie. Zmienne, które mają typ BaseClass , nadal uzyskują dostęp do elementów członkowskich BaseClass, a zmienna, która ma typ DerivedClass , nadal uzyskuje dostęp do elementów członkowskich w DerivedClass pierwszej kolejności, a następnie należy wziąć pod uwagę elementy członkowskie dziedziczone z BaseClassklasy .

Aby pominąć ostrzeżenie, dodaj new modyfikator do definicji Method2 w DerivedClasspliku , jak pokazano w poniższym kodzie. Modyfikator można dodać przed lub po public.

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

Uruchom ponownie program, aby sprawdzić, czy dane wyjściowe nie uległy zmianie. Sprawdź również, czy ostrzeżenie nie jest już wyświetlane. Używając metody new, stwierdzasz, że wiesz, że element członkowski, który modyfikuje, ukrywa element członkowski dziedziczony z klasy bazowej. Aby uzyskać więcej informacji na temat ukrywania nazwy przez dziedziczenie, zobacz nowy modyfikator.

Aby porównać to zachowanie z efektami używania metody override, dodaj następującą metodę do DerivedClassmetody . override Modyfikator można dodać przed lub po public.

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

virtual Dodaj modyfikator do definicji Method1 w pliku .BaseClass virtual Modyfikator można dodać przed lub po public.

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

Uruchom ponownie projekt. Zwróć szczególną uwagę na dwa ostatnie wiersze następujących danych wyjściowych.

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

Użycie override modyfikatora umożliwia bcdc dostęp do metody zdefiniowanej Method1 w pliku DerivedClass. Zazwyczaj jest to pożądane zachowanie w hierarchiach dziedziczenia. Obiekty, które mają wartości utworzone na podstawie klasy pochodnej, mają używać metod zdefiniowanych w klasie pochodnej. To zachowanie można osiągnąć za pomocą polecenia override w celu rozszerzenia metody klasy bazowej.

Poniższy kod zawiera pełny przykład.

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");  
        }  
    }  
}  

Poniższy przykład ilustruje podobne zachowanie w innym kontekście. W przykładzie zdefiniowano trzy klasy: klasę bazową o nazwie Car i dwie klasy, które pochodzą z niej, ConvertibleCar i Minivan. Klasa bazowa zawiera metodę DescribeCar . Metoda wyświetla podstawowy opis samochodu, a następnie wywołuje metodę ShowDetails w celu podania dodatkowych informacji. Każda z trzech klas definiuje metodę ShowDetails . Modyfikator new służy do definiowania ShowDetails w ConvertibleCar klasie . Modyfikator override służy do definiowania ShowDetails w Minivan klasie .

// 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.");  
    }  
}  

Przykład sprawdza, która wersja programu jest wywoływana ShowDetails . Poniższa metoda TestCars1deklaruje wystąpienie każdej klasy, a następnie wywołuje DescribeCar każde wystąpienie.

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 generuje następujące dane wyjściowe. Zwróć szczególną uwagę na wyniki funkcji car2, które prawdopodobnie nie są oczekiwane. Typ obiektu to ConvertibleCar, ale DescribeCar nie uzyskuje dostępu do wersji zdefiniowanej ShowDetailsConvertibleCar w klasie, ponieważ ta metoda jest zadeklarowana za pomocą new modyfikatora, a nie override modyfikatora. W związku ConvertibleCar z tym obiekt wyświetla ten sam opis co Car obiekt. Skontrastuj wyniki dla car3elementu , który jest obiektem Minivan . W tym przypadku ShowDetails metoda zadeklarowana w Minivan klasie zastępuje ShowDetails metodę zadeklarowaną w Car klasie, a wyświetlany opis opisuje 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 Tworzy listę obiektów, które mają typ Car. Wartości obiektów są tworzone z Carklas , ConvertibleCari Minivan . DescribeCar element jest wywoływany dla każdego elementu listy. Poniższy kod przedstawia definicję elementu 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("----------");  
    }  
}  

Zostaną wyświetlone następujące dane wyjściowe. Zwróć uwagę, że jest on taki sam jak dane wyjściowe wyświetlane przez TestCars1element . Metoda ShowDetails klasy nie jest wywoływana, niezależnie od tego, czy typ obiektu to ConvertibleCar, jak w , lub Car, jak w TestCars1TestCars2.ConvertibleCar car3 Z drugiej strony wywołuje metodę ShowDetails z Minivan klasy w obu przypadkach, niezależnie od tego, czy ma typMinivan, czy typ Car.

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

Metody TestCars3 i TestCars4 uzupełnij przykład. Metody te są wywoływane ShowDetails bezpośrednio, najpierw z obiektów zadeklarowanych jako typ ConvertibleCar i Minivan (TestCars3), a następnie z obiektów zadeklarowanych jako typ Car (TestCars4). Poniższy kod definiuje te dwie metody.

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();  
}  

Metody generują następujące dane wyjściowe, które odpowiadają wynikam z pierwszego przykładu w tym temacie.

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

Poniższy kod przedstawia kompletny projekt i jego dane wyjściowe.

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.");  
        }  
    }  
  
}  

Zobacz też