Udostępnij za pośrednictwem


Znajomość, kiedy należy używać 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 za pomocą słów kluczowych new i override. 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: BaseClass i DerivedClass. 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 Method1, chyba że używasz rzutowania. Zmienna dc może uzyskiwać dostęp zarówno do Method1, jak 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 BaseClass. Podpis tej metody jest zgodny z podpisem metody Method2 w 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 Method2 w DerivedClass zacienia metodę Method2 w BaseClass. 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 BaseClass, a zmienna, która ma typ DerivedClass, nadal najpierw uzyskuje dostęp do elementów w DerivedClass, a następnie bierze pod uwagę elementy dziedziczone z BaseClass klasy.

Aby pominąć ostrzeżenie, dodaj modyfikator new do definicji Method2 w pliku DerivedClass, 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. Stwierdzając, że używasz new, potwierdzasz, iż jesteś świadomy, że element członkowski, który modyfikujesz, ukrywa element członkowski dziedziczony z klasy bazowej. Aby uzyskać więcej informacji na temat ukrywania nazw przez dziedziczenie, zobacz new Modifier.

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

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

Dodaj modyfikator virtual do definicji Method1 w 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 testuje, która wersja ShowDetails jest wywoływana. Poniższa metoda TestCars1 deklaruje wystąpienie każdej klasy, a następnie wywołuje DescribeCar na każdym wystąpieniu.

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 ShowDetails zdefiniowanej w klasie ConvertibleCar, ponieważ ta metoda jest zadeklarowana z modyfikatorem new, a nie override. W rezultacie ConvertibleCar obiekt wyświetla ten sam opis, co Car obiekt. Skontrastuj wyniki dla car3, 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 klas Car, ConvertibleCar i Minivan. Funkcja DescribeCar jest wywoływana 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 taki sam jak wynik wyświetlany przez TestCars1. Metoda ShowDetails klasy ConvertibleCar nie jest wywoływana, niezależnie od tego, czy typ obiektu to ConvertibleCar, jak w TestCars1, czy Car, jak w TestCars2. 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łniają przykład. Te metody wywołują 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 także