了解使用 Override 和 New 關鍵字的時機 (C# 程式設計手冊)

在 C# 中,衍生類別的方法名稱可以與基底類別的方法名稱相同。 您可以使用 newoverride 關鍵字來指定方法的互動方式。 override 修飾詞會「延伸」基底類別的 virtual 方法,new 修飾詞則會「隱藏」可存取的基底類別方法。 本主題的範例會說明其間的差異。

在主控台應用程式中,宣告 BaseClassDerivedClass 這兩個類別。 DerivedClass 繼承自 BaseClass

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

Main 方法中,宣告 bcdcbcdc 變數。

  • bc 屬於 BaseClass 類型,而其值為 BaseClass 類型。

  • dc 屬於 DerivedClass 類型,而其值為 DerivedClass 類型。

  • bcdc 屬於 BaseClass 類型,而其值為 DerivedClass 類型。 這是值得注意的變數。

由於 bcbcdc 具有 BaseClass 類型,因此只能直接存取 Method1 (除非您使用轉型)。 dc 變數可以同時存取 Method1Method2。 下列程式碼顯示這些關聯性。

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。 此方法的簽章與 DerivedClassMethod2 方法簽章相符。

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

由於 BaseClass 現已具備 Method2 方法,因此您可以為 BaseClassbcbcdc 變數新增第二個呼叫陳述式,如下列程式碼所示。

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

在建置專案時,您會發現在新增 BaseClassMethod2 方法時會產生警告。 警告指出,DerivedClassMethod2 方法會隱藏 BaseClassMethod2 方法。 如果您想要這種結果,建議您在 Method2 定義中使用 new 關鍵字。 或者,您可以重新命名其中一個 Method2 方法來解決警告,但不一定總是可行。

請先執行程式並查看其他呼叫陳述式所產生的輸出,之後再加入 new。 即會顯示下列結果。

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

new 關鍵字會保留產生該輸出的關聯性,但是它會隱藏警告。 如果變數具有 BaseClass 類型,其會繼續存取 BaseClass 的成員,而含 DerivedClass 類型的變數則會繼續優先存取 DerivedClass 中的成員,然後才考慮繼承自 BaseClass 的成員。

若要隱藏警告,請將 new 修飾詞新增至 DerivedClass 中的 Method2 定義,如下列程式碼所示。 您可在 public 之前或之後的位置新增修飾詞。

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

再次執行程式,確認輸出尚未變更, 另請確認警告已不再顯示。 使用 new 時,即表示您了解此關鍵字所修改的成員,會隱藏繼承自基底類別的成員。 如需透過繼承隱藏名稱的詳細資訊,請參閱 new 修飾詞

若要比較這項行為與使用 override 的效果,請將下列方法新增至 DerivedClass。 您可在 public 之前或之後的位置新增 override 修飾詞。

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

請將 virtual 修飾詞新增至 BaseClassMethod1 定義。 您可在 public 之前或之後的位置新增 virtual 修飾詞。

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

重新執行專案。 請特別注意下列輸出的最後兩行。

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

使用 override 修飾詞時,可讓 bcdc 存取 DerivedClass 中所定義的 Method1 方法。 一般而言,這是繼承階層架構所要的結果。 您希望物件的值都是從衍生類別所建立,以使用衍生類別中定義的方法。 您可以使用 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 的基底類別,以及兩個由此衍生的 ConvertibleCarMinivan 類別。 基底類別包含 DescribeCar 方法。 此方法會顯示車輛的基本描述,然後呼叫 ShowDetails 來提供其他資訊。 這三個類別都會定義 ShowDetails 方法。 new 修飾詞可用來定義 ConvertibleCar 類別中的 ShowDetailsoverride 修飾詞可用來定義 Minivan 類別中的 ShowDetails

// 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 不會存取 ConvertibleCar 類別中所定義的 ShowDetails 版本,因為該方法是使用 new 修飾詞來進行宣告,而不是 override 修飾詞。 如此一來,ConvertibleCar 物件顯示的描述會與 Car 物件相同。 car3 (也就是 Minivan 物件) 的結果則相反。 在此情況下,Minivan 類別中所宣告的 ShowDetails 方法會覆寫 Car 類別中所宣告的 ShowDetails 方法,而且顯示的描述為休旅車。

// 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 類型的物件清單。 物件的值是從 CarConvertibleCarMinivan 類別具現化而得。 清單上的每個項目都會呼叫 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 顯示的輸出相同。 不論物件的類型是 ConvertibleCar (如 TestCars1 中),或 Car (如 TestCars2 中),系統均不會呼叫 ConvertibleCar 類別的 ShowDetails 方法。 相反地,car3 會從 Minivan 呼叫這兩種類別的 ShowDetails 方法,不論其為 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.  
// ----------  

TestCars3TestCars4 方法會完成範例。 這些方法會先從宣告為具有 ConvertibleCarMinivan 類型的物件 (TestCars3),再從物件宣告為具有 Car 類型的物件 (TestCars4) 直接呼叫 ShowDetails。 下列程式碼會定義這兩種方法。

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

另請參閱