共用方式為


瞭解何時使用 Override 和 New 關鍵詞 (C# 程式設計手冊)

在 C# 中,衍生類別中的方法可以擁有與基類中方法相同的名稱。 您可以使用 新的override 關鍵詞來指定方法的互動方式。 override修飾詞會擴充基類virtual方法,而 new 修飾詞會隱藏可存取的基類方法。 本主題的範例會說明差異。

在主控台應用程式中,宣告下列兩個類別和 BaseClassDerivedClassDerivedClass 繼承自 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。 這個方法的簽章符合 中 Method2方法的DerivedClass簽章。

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

因為 BaseClass 現在有 Method2 方法,因此可以針對 BaseClass 變數 bcbcdc新增第二個呼叫語句,如下列程式代碼所示。

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 修飾詞新增至 Method2DerivedClass 中的定義,如下列程式碼所示。 修飾詞可以新增在 public之前或之後。

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

再次執行程式,以確認輸出尚未變更。 也請確認警告不再出現。 藉由使用 new,您表示知道其所修改的成員會隱藏繼承自基類的成員。 如需透過繼承隱藏名稱的詳細資訊,請參閱 new Modifier

虛擬和覆寫關鍵詞

若要將此行為與使用 override的效果形成對比,請將下列方法新增至 DerivedClassoverride修飾詞可以在 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.
  
    Car 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 方法,而顯示的描述會描述休旅車。

// 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 所顯示的輸出相同。 無論物件的類型是ShowDetails(如ConvertibleCar所示)還是ConvertibleCar(如TestCars1所示),都不會呼叫Car類別的TestCars2方法。 相反地,無論是型別car3還是型別ShowDetailsMinivan在這兩種情況下都會從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完成範例。 這些方法會直接呼叫 ShowDetails ,首先從宣告為 型別 ConvertibleCarMinivan 的物件 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.");  
        }  
    }  
  
}  

另請參閱