培训
C# 语言旨在使 不同库中的基 类和派生类之间的版本控制能够发展和维护向后兼容性。 例如,这意味着,在与派生类中的成员同名的基 类 中引入新成员完全受 C# 支持,并且不会导致意外行为。 它还意味着类必须显式声明某方法是要替代一个继承方法,还是本身就是一个隐藏具有类似名称的继承方法的新方法。
在 C# 中,派生类可以包含与基类方法同名的方法。
如果派生类中的方法前面没有 新 关键字或 重写 关键字,编译器将发出警告,并且该方法的行为就像存在关键字一样
new
。如果派生类中的方法前面有
new
关键字,则该方法定义为独立于基类中的方法。如果派生类中的方法前面有
override
关键字,则派生类的对象将调用该方法而不是基类方法。若要将
override
关键字应用于派生类中的方法,基类方法必须定义为virtual。可以使用关键字从派生类中调用基类
base
方法。override
还可以将和virtual
new
关键字应用于属性、索引器和事件。
默认情况下,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.
有关 new
和 override
的更多示例,请参阅 “了解何时使用 Override 和 New 关键字”。