使用英语阅读

通过


使用替代和新关键字进行版本控制 (C# 编程指南)

C# 语言旨在使 不同库中的基 类和派生类之间的版本控制能够发展和维护向后兼容性。 例如,这意味着,在与派生类中的成员同名的基 中引入新成员完全受 C# 支持,并且不会导致意外行为。 它还意味着类必须显式声明某方法是要替代一个继承方法,还是本身就是一个隐藏具有类似名称的继承方法的新方法。

在 C# 中,派生类可以包含与基类方法同名的方法。

  • 如果派生类中的方法前面没有 关键字或 重写 关键字,编译器将发出警告,并且该方法的行为就像存在关键字一样 new

  • 如果派生类中的方法前面有 new 关键字,则该方法定义为独立于基类中的方法。

  • 如果派生类中的方法前面有 override 关键字,则派生类的对象将调用该方法而不是基类方法。

  • 若要将override关键字应用于派生类中的方法,基类方法必须定义为virtual

  • 可以使用关键字从派生类中调用基类 base 方法。

  • override还可以将和virtualnew关键字应用于属性、索引器和事件。

默认情况下,C# 方法不是虚拟方法。 如果方法声明为虚拟方法,则继承该方法的任何类都可以实现其自己的版本。 若要使方法成为虚拟方法, virtual 修饰符用于基类的方法声明中。 然后,派生类可以使用关键字替代基虚拟方法 override ,或使用关键字隐藏基类 new 中的虚拟方法。 如果未指定override关键字或new关键字,编译器将发出警告,并且派生类中的方法将遮蔽基类中的方法。

若要在实践中演示这一点,请假设公司 A 已创建一个名为 GraphicsClass(程序使用)的类。 GraphicsClass 如下所示:

class GraphicsClass
{
    public virtual void DrawLine() { }
    public virtual void DrawPoint() { }
}

公司使用此类,并且你在添加新方法时将其用于派生自己的类:

class YourDerivedGraphicsClass : GraphicsClass
{
    public void DrawRectangle() { }
}

你的应用程序运行正常,未出现问题,直到公司 A 发布了 GraphicsClass 的新版本,类似于以下代码:

class GraphicsClass
{
    public virtual void DrawLine() { }
    public virtual void DrawPoint() { }
    public virtual void DrawRectangle() { }
}

GraphicsClass的新版本现在包含一个名为DrawRectangle的方法。 最初,没有任何事情发生。 新版本仍与旧版本二进制兼容。 部署的任何软件都将继续工作,即使新类安装在这些计算机系统上也是如此。 对该方法 DrawRectangle 的任何现有调用将继续引用你的派生类中的版本。

但是,使用新版本 GraphicsClass重新编译应用程序后,将收到来自编译器 CS0108 的警告。 此警告提示,必须考虑你所期望的 DrawRectangle 方法在应用程序中的工作方式。

如果需要自己的方法替代新的基类方法,请使用 override 关键字:

class YourDerivedGraphicsClass : GraphicsClass
{
    public override void DrawRectangle() { }
}

关键字 override 确保派生自 YourDerivedGraphicsClass 的任何对象都将使用派生类 DrawRectangle版本的 。 派生自 YourDerivedGraphicsClass 的对象仍可以使用基关键字访问其基类版本 DrawRectangle

base.DrawRectangle();

如果您不希望您的方法覆盖新的基类方法,请注意以下事项。 若要避免两种方法之间的混淆,可以重命名方法。 这很耗时且容易出错,在某些情况下并不实用。 但是,如果项目相对较小,则可以使用 Visual Studio 的重构选项重命名方法。 有关详细信息,请参阅重构类和类型(类设计器)。

或者,可以使用派生类定义中的关键字 new 来阻止警告:

class YourDerivedGraphicsClass : GraphicsClass
{
    public new void DrawRectangle() { }
}

使用 new 关键字告诉编译器你的定义覆盖了基类中包含的定义。 这是默认行为。

替代和方法选择

在类上命名方法时,如果多个方法与调用兼容,则 C# 编译器会选择调用的最佳方法,例如当有两个具有相同名称的方法和与传递的参数兼容的参数时。 以下方法兼容:

public class Derived : Base
{
    public override void DoWork(int param) { }
    public void DoWork(double param) { }
}

Derived 的一个实例中调用 DoWork 时,C# 编译器将首先尝试使该调用与最初在 Derived 上声明的 DoWork 版本兼容。 重写方法不被视为在类上声明,它们是在基类上声明的方法的新实现。 仅当 C# 编译器无法将方法调用与 Derived 上的原始方法匹配时,才尝试将该调用与具有相同名称和兼容参数的替代方法匹配。 例如:

int val = 5;
Derived d = new();
d.DoWork(val);  // Calls DoWork(double).

由于变量 val 可以隐式转换为 double 型,因此 C# 编译器调用 DoWork(double) 而不是 DoWork(int)。 有两种方法可以避免这种情况。 首先,请避免声明与虚拟方法同名的新方法。 其次,您可以通过将Derived实例强制转换为Base,从而指示 C# 编译器在基类方法列表中查找并调用虚拟方法。 由于是虚方法,因此将调用 Derived 上的 DoWork(int) 的实现。 例如:

((Base)d).DoWork(val);  // Calls DoWork(int) on Derived.

有关 newoverride 的更多示例,请参阅 “了解何时使用 Override 和 New 关键字”

另请参阅


其他资源

培训

模块

实现类继承 - Training

了解如何使用基类和派生类创建类层次结构,以及如何使用“new”、“virtual”、“abstract”和“override”关键字隐藏或替代派生类的成员。