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

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

但实际上我发现如果派生类具有相同的命名成员,派生类会自动隐藏派生成员。 那么同一个命名派生类成员中新关键字的主要优点和问题是什么?

如您所知, 不需要新function。 它是可选的 ,如果你不使用它,你会收到警告。 你完全正确地注意到这是一个奇怪的设计决定。

此设计决策的目的是帮助缓解一类被称为“脆弱基类”问题的问题。 这是该问题的一个版本:

Foo Corporation创建了一个类Frobber并将其发布在Foo.DLL 1.0版中:

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

你工作的Bar Corporation是Blobbers。 Blobber可以完成Frobber可以做的所有事情,但此外,它也可以使用Blobnicate。 所以你决定从FooCorp重新使用Frobnicate的实现,并添加一些额外的function:

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

Foo公司意识到人们喜欢Blobnicate,他们决定运送Foo.DLL v2.0:

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

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

我们将“new”设置为可选,以便在不更改源代码的情况下隐藏基类方法是合法的。 如果我们将其设为非法,那么FooCorp会因升级而破坏您的构建。 但是我们会发出警告,以便您知道您可能会意外地这样做 。 然后,您可以仔细检查代码; 如果您确定您的Blobnicate实现现在是多余的,则可以将其删除。 如果仍然良好,您可以将其标记为“新”并消除警告。

合理? 这是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关键字的情况下隐藏基类的成员,但是无法确定是否确实要隐藏该成员,或者您是否真的想要覆盖该成员。

明确表达总是更好。 这样,下一个开发人员就会确切地知道你的意图。

我在creditCard类中创建了CardNumber方法,之后如果我想在同一方法中进行更改,即目前我使用卡号作为字符串和整数的组合并返回字符串,但在此之后客户需要整数卡号时,您可以使用新密钥隐藏单词或方法而不更改代码。

  class CreditCard { public string CardNumber(string value) { return "XXX57879"; } } class GoldCard : CreditCard { public new int CardNumber(string value) { return 1234567; } }