Share via


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

更新:2011 年 5 月

在 C# 中,衍生類別中的方法可以與基底類別中的方法同名。 您可以使用 newoverride 關鍵字來指定方法互動的方式。 override 修飾詞會「擴充」(Extend) 基底類別方法,而 new 修飾詞則加以「隱藏」(Hide)。 本主題中的範例會說明此差異。

在主控台應用程式中,宣告下列兩個類別: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 中。 此方法的簽章與 DerivedClass 中的 Method2 方法簽章相符。

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

因為BaseClass現在有Method2方法,可以加入 BaseClass變數bc和bcdc 的第二個呼叫陳述式,如下列程式碼所示。

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

當您產生專案時,您將看到,在 BaseClass 中加入 Method2 方法會產生警告。 警告表示,DerivedClass 中的 Method2 方法將 Method2 方法隱藏在 BaseClass 中。 如果想要產生這種結果,建議您使用 Method2 定義中的 new 關鍵字。 或者,您可以重新命名其中一個 Method2方法來解決警告,但這個方法並不實用。

加入 new 前,請執行程式以查看其他呼叫陳述式所產生的輸出。 將顯示下列結果。

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

new 關鍵字保留產生該輸出的關聯性,但會隱藏警告。 型別為 BaseClass 的變數會繼續存取 BaseClass 的成員,而型別為 DerivedClass 的變數則首先繼續存取 DerivedClass 的成員,然後再考慮繼承自 BaseClass 的成員。

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

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

再次執行程式以確認輸出沒有改變。 同時驗證不會再出現警告。 使用 new 就明確表示您知道它所修改的成員會隱藏繼承自基底類別的成員 如需關於透過繼承隱藏名稱的詳細資訊,請參閱new 修飾詞 (C# 參考).

若要將這個行為與使用 override 的影響進行對比,請將下列方法加入至 DerivedClass。 可在 public 之前或之後加入的 override 修飾詞。

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

將 virtual 修飾詞新增至Method1 (BaseClass 中) 的定義。 可在 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 的基底類別和兩個衍生自此基底類別的類別 ConvertibleCar 和 Minivan。 基底類別包含DescribeCar方法。 此方法會顯示汽車的基本描述,然後呼叫 ShowDetails 提供其他資訊。 三個類別中,每一個都定義一個ShowDetails方法。 new 修飾詞用於定義 ConvertibleCar 類別中的 ShowDetails。 override 修飾詞用於定義 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 的物件清單。 物件的值是從 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 所顯示的輸出相同。 不論物件的型別是 TestCars1 中的 ConvertibleCar,還是 TestCars2 中的 Car,都不會呼叫 ConvertibleCar 類別的 ShowDetails 方法。 相反的,無論 car3 的型別是 Minivan 還是 Car,它在這兩種情況下都會呼叫 Minivan 類別中的 ShowDetails 方法。

// 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 方法。 這些方法會先從型別宣告為 ConvertibleCar 和 Minivan (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.");
        }
    }

}

請參閱

參考

類別和結構 (C# 程式設計手冊)

使用 Override 和 New 關鍵字進行版本控制 (C# 程式設計手冊)

base (C# 參考)

abstract (C# 參考)

概念

C# 程式設計手冊

變更記錄

日期

記錄

原因

2011 年 5 月

修訂文字和範例

客戶回函。