在 C# 中,衍生類別中的方法可以擁有與基類中方法相同的名稱。 您可以使用 新的 和 override 關鍵詞來指定方法的互動方式。
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 ,宣告變數 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。 這個方法的簽章符合 中 Method2方法的DerivedClass簽章。
public void Method2()
{
Console.WriteLine("Base - Method2");
}
因為 BaseClass 現在有 Method2 方法,因此可以針對 BaseClass 變數 bc 和 bcdc新增第二個呼叫語句,如下列程式代碼所示。
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 修飾詞新增至 Method2 在 DerivedClass 中的定義,如下列程式碼所示。 修飾詞可以新增在 public之前或之後。
public new void Method2()
{
Console.WriteLine("Derived - Method2");
}
再次執行程式,以確認輸出尚未變更。 也請確認警告不再出現。 藉由使用 new,您表示知道其所修改的成員會隱藏繼承自基類的成員。 如需透過繼承隱藏名稱的詳細資訊,請參閱 new Modifier。
虛擬和覆寫關鍵詞
若要將此行為與使用 override的效果形成對比,請將下列方法新增至 DerivedClass。
override修飾詞可以在 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的物件清單。 物件的值會從 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 所顯示的輸出相同。 無論物件的類型是ShowDetails(如ConvertibleCar所示)還是ConvertibleCar(如TestCars1所示),都不會呼叫Car類別的TestCars2方法。 相反地,無論是型別car3還是型別ShowDetails,Minivan在這兩種情況下都會從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.
// ----------
方法TestCars3和TestCars4完成範例。 這些方法會直接呼叫 ShowDetails ,首先從宣告為 型別 ConvertibleCar 和 Minivan 的物件 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.");
}
}
}