在与基类成员同名的派生类成员中使用new关键字的好处

本文关键字:成员 new 关键字 派生 基类 | 更新日期: 2023-09-27 18:15:20

c#语言规范说,如果我继承了一个类,并且基类和派生类具有相同签名的相同命名成员,那么我必须使用new关键字来隐藏基类成员(还有另一种方法是在基类和派生类成员中使用virtual和override关键字)。

但在实践中,我发现派生类自动隐藏派生成员,如果它有相同的命名成员。那么,在同名派生类成员中使用new关键字的主要好处和问题是什么呢?

在与基类成员同名的派生类成员中使用new关键字的好处

New不是必需的,正如您注意到的。它是可选的,如果你不使用它,你会得到一个警告。你完全正确地注意到,乍一看,这是一个奇怪的设计决定。

这个设计决策的目的是帮助减轻一类被称为"脆弱基类"的问题。下面是这个问题的一个版本:

Foo Corporation创建了一个类Frobber并在Foo. dll版本1.0中发布:

namespace FooCorp
{
  public class Frobber
  {
    public void Frobnicate() { ... }
    ...

你所在的酒吧公司生产Blobbers。Blobber可以做Frobber所能做的一切,但除此之外,它还可以blobate。因此,您决定重用FooCorp的Frobnicate实现,并添加一些额外的功能:

namespace BarCorp
{
  public class Blobber : FooCorp.Frobber
  {
    public void Blobnicate() { ... }
    ...

Foo公司意识到人们喜欢blobicate,他们决定发布Foo. dll v2.0:

namespace FooCorp
{
  public class Frobber
  {
    public void Frobnicate() { ... }
    public void Blobnicate() { ... }
    ...

当您得到一个新版本的Foo.DLL并重新编译时,您希望被告知您现在意外地引入了一个新方法,该方法遮蔽了基类方法。这可能是一件危险的事情;在编写类时假设基类是一个Blobnicator,但显然现在它也是一个Blobnicator !这个事实可能会破坏您的客户,他们可能在打算调用派生类版本时意外调用基类版本。

我们使"new"成为可选的,因此可以合法地在不更改源代码的情况下隐藏基类方法。如果我们将其定为非法,那么FooCorp将会用他们的升级破坏你的构建。但我们将其作为警告,以便您知道您可能会不小心这样做。然后,您可以仔细检查代码;如果您认为您的blobnate实现现在是冗余的,您可以删除它。如果它仍然是好的,你可以标记为"新的",并消除警告。

有意义吗?这是c#的一个微妙特性,使它适合于大规模的多版本面向组件的软件。

使用new的好处是使您的意图清晰。

然而,除非你真的需要隐藏成员,通常最好使用virtual/override来多态地使用成员。通过隐藏,new成员只能通过派生类的引用来使用。如果您正在通过一个基本引用工作,您将继续获得基本行为,并且可能是意想不到的

class Foo 
{
     public void M() { Console.WriteLine("Foo"); }
}
class Bar : Foo
{
     public new void M() { Console.WriteLine("Bar"); }
}
Bar bar = new Bar(); 
bar.M(); // writes Bar
Foo foo = new Bar(); // still an instance of Bar
foo.M(); // writes Foo, does not use "new" method defined in Bar

如果在Foo中使用virtual声明方法,在Bar中使用override声明方法,那么无论在哪种情况下,方法调用都将始终是"Bar"。

new关键字可以让你的意图更具体。

虽然默认的行为是隐藏基类的成员而不使用new关键字,但是没有办法告诉您是真的想隐藏成员还是实际上想重写成员。

明确总是更好的。这样,下一个开发人员将确切地知道您想要做什么。