为什么要使用“new”隐藏方法

本文关键字:隐藏 方法 new 为什么 | 更新日期: 2023-09-27 18:06:40

可能重复:
C#-方法签名中的新关键字

假设我有三个班:爷爷,爸爸,儿子。儿子继承父亲的遗产,父亲继承祖父的遗产。

每个类实现foo。

// GrandDad class: 
public virtual void foo()
// Dad class: 
new public virtual void foo()
// Son class: 
public override void foo()

我不明白为什么爸爸会使用new关键字。据我所知,使用new隐藏了一个方法。你为什么要这么做?

我阅读了MSDN对new的解释,但讨论只是机械的,而不是体系结构的。

感谢

为什么要使用“new”隐藏方法

您提供的代码不是一个很好的例子,但"new"的一个常见用例是,如果修改基类以添加新方法(通常是您无法控制的基类(,但您有现有的代码需要用相同的名称调用派生类方法。将派生类方法更改为"new"可以使您与公共接口的现有使用者保持兼容。

希望下面的例子能为您提供新关键字的用途。这实际上取决于功能的需求。

public class Base
{
    public virtual void SomeMethod()
    {
    }
}
public class Derived : Base
{
    public override void SomeMethod()
    {
    }
}
...
Base b = new Derived();
b.SomeMethod();

如果Derived.SomeMethod重写Base.SomeMethod.,则最终将调用该方法

现在,如果使用new关键字而不是override,派生类中的方法不会覆盖基类中的方法,它只是隐藏它

public class Base
{
    public virtual void SomeOtherMethod()
    {
    }
}
public class Derived : Base
{
    public new void SomeOtherMethod()
    {
    }
}
...

Base b = new Derived();
Derived d = new Derived();
b.SomeOtherMethod();
d.SomeOtherMethod();

将首先调用Base.SomeOtherMethod,然后调用Derived.SomeOtherMethod。它们实际上是两个完全独立的方法,碰巧具有相同的名称,而不是覆盖基方法的派生方法。

一种场景,当你在更多层(Control、Button、RedButton(上有一个类的层次结构时,你可能"有用",并且你希望给定类(Button(的任何子类都受到限制,以更改从Control继承的、由Button修改的通用行为;按照按钮规则而不是控制规则玩,否则RedButton可以成为TreeViewControl:(。。。;当您继承实现时可能会发生这种情况:(

还有其他更干净的技术,比如"策略模式"(聚合行为,而不是继承(,用于处理前面的场景。

我将给出一个超越"它做什么"的例子,并介绍它何时有用。

是否需要您的一个业务类成为容器?例如,也许你想这样做:

Person aperson = new Person();
aperson.Add(anAddress);
aperson.Add(anAddress);

如果您希望Add((方法在插入时执行一些特定于业务的逻辑,您可以选择。要么你自己实现容器功能(当然(,要么让Person类从容器继承:

public class Person : List<Address> { }

现在你可以在两个条件下得到你想要的行为:

  1. 不要按Person类的基类型(List(引用它,并且
  2. 使用"new"关键字可以隐藏基列表的"add"操作

新关键字是必需的,因为List.Add(…(不是虚拟的。这样做有点邪恶(可能很糟糕(,但在这种特殊情况下大多很酷。在您编写的"new Add(…("方法中,包含您的业务逻辑,然后调用基本Add(..(方法。

想让这个更酷吗?制作一棵树:

public class Node : List<Node> { }

在电子游戏中,我在写游戏树时使用过这个技巧。

// GrandDad: 
public void CanIWatchTv() {
   return age > 5;
}
// Dad: Grandpa is too liberal
public override void CanIWatchTv() {
   return age > 12;
}
// Son: I don't care what dad says!
public new void CanIWatchTv() {
   return true;
}

换句话说,当你完全想覆盖(而不是关键字(基本方法正在做的事情而不是扩展它时,你应该使用new

在现实世界中,它应该谨慎使用,因为它违反了继承原则(是否应该允许Son覆盖他的Dad——答案取决于你是儿子还是父亲,但在完美的世界中,不应该这样(。

我的理解是,随着时间的推移,主要动机之一与库的版本控制有关。

1月1日:X公司发布了作为超级图书馆v1.0的一部分的类Dad。

class Dad
{
    virtual public void Foo() { ... };
}

2月1日:您购买了Super Library v1.0并创建子类Son。

class Son : Dad
{
    public void Bar() { ... };
} 

3月1日:X公司发布了Super Library 2.0,添加了一个名为Bar的新方法。他们不知道你也创建了一个方法Bar,他们的Bar完全无关。

class Dad
{
    virtual public void Foo() { ... };
    virtual public void Bar() { ... };
}

5月1日:你购买了Super Library v2.0并根据它重新编译你的Son类。令你惊讶的是,你发现他们添加了一个名为Bar的方法,与你的方法冲突。为了最大限度地降低回归风险,您现在将Bar方法标记为,以将其与X公司的Bar区分开来。两者和平共处,尽管奇怪的是它们有着相同的名字。他们现有的代码将按预期调用他们的Bar,而您现有的代码则按预期调用您的Bar。

class Son : Dad
{
    new public void Bar() { ... };
}