在 C# 中,派生类中的方法可以具有与基类中的方法相同的名称。 可以使用 新的 关键字和 重写 关键字来指定方法的交互方式。 修饰符 override
扩展 基类 virtual
方法,修饰符 new
隐藏 可访问的基类方法。 本主题中的示例说明了差异。
在控制台应用程序中,声明以下两个类, 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
. 此方法的签名与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
,断言你知道它修饰的成员将隐藏从基类继承的成员。 有关通过继承隐藏名称的详细信息,请参阅 新的修饰符。
若要将此行为与使用 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.
ConvertibleCar 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
类中声明的 Car
方法,显示的说明描述小型货车。
// 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.");
}
}
}