为什么要使用 `new` 隐藏一个方法?

7

可能是重复问题:
C# - 方法签名中的new关键字

假设我有三个类:GrandDad,Dad,Son。Son继承自Dad,Dad继承自GrandDad。

每个类都实现了foo。

// GrandDad class: 
public virtual void foo()

// Dad class: 
new public virtual void foo()

// Son class: 
public override void foo()

我不理解爸爸为什么要使用new关键字。据我所知,使用new会隐藏一个方法。你为什么要这样做呢?
我阅读了MSDN对new的解释,但讨论只是机械的,而不是架构上的。
谢谢。

通常情况下,您会在非虚拟方法的上下文中看到它。那是一个奇怪的情况。 - Jeff Mercado
这个SO问题的答案可能会有所帮助: https://dev59.com/jnNA5IYBdhLWcg3wUL_n - Ben Gribaudo
6个回答

3

你提供的代码并不是一个很好的例子,但是 'new' 的一个普遍用法是,如果基类被修改以添加一个新方法(通常是你无法控制的基类),但你有现有的代码需要调用与名称相同的派生类方法。将派生类方法更改为 'new' 允许你保持与公共接口的现有消费者兼容。


0
希望下面的例子能够让你了解 new 关键字的用途。它真正的作用取决于功能需求。
public class Base
{
    public virtual void SomeMethod()
    {
    }
}

public class Derived : Base
{
    public override void SomeMethod()
    {
    }
}

...

Base b = new Derived();
b.SomeMethod();

如果派生类重写了基类的方法,那么最终会调用Derived.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。它们实际上是两个完全独立的方法,恰好具有相同的名称,而不是派生方法覆盖基本方法。


0
我将举一个超越“它是什么”的例子,并涵盖它何时有用。
您是否曾经需要将您的业务类之一作为容器?例如,也许您想要做到这一点:
Person aperson = new Person();
aperson.Add(anAddress);
aperson.Add(anAddress);

如果你想要Add()方法在插入时执行一些特定于业务的逻辑,你有两个选择。 要么自己实现容器功能(呸),要么让Person类继承容器:

public class Person : List<Address> { }

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

  1. 不要通过基类(List)引用Person类,以及...
  2. 使用'new'关键字隐藏基类List的'add'操作。

'new'关键字是必需的,因为List.Add(...)不是虚方法。这样做可能有点邪恶(可能不好),但在这种特殊情况下非常酷。在"new Add(...)"方法中,你可以编写业务逻辑,然后调用基类的Add(...)方法。

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

public class Node : List<Node> { }

在视频游戏中,我在编写游戏树时使用了这个技巧。

0

当你有多层类的层次结构(Control,Button,RedButton)并且希望给定类(Button)的任何子类都受到限制以更改从Control继承的通用行为时,这种情况可能会“有用”,该行为由Button修改;按照Button规则而不是Control规则进行操作,否则RedButton可能会变成TreeViewControl :) ...; 当您继承实现时,可能会发生这种情况。

还有其他更干净的技术,例如“策略模式”(聚合行为而不是继承),可用于处理先前的情况。


-1

好吧,这里有一个你想要这样做的原因:

// 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
在现实世界的场景中,它应该被谨慎使用,因为它违反了继承的原则(儿子是否应该允许覆盖他的父亲-答案取决于您是儿子还是父亲,但在完美的世界中,不应该这样做)。

@Downvoter:我预料到这种解释方式可能不会得到所有人的认同,但如果您能解释一下原因,也许我可以做出改进... - Mrchief
如果类被正确设计,将 derivedClassVariable.DoSomething(whatever) 替换为 ((BaseClass)derivedClassVariable).DoSomething(whatever)通常会导致编译错误或者行为相同。有些情况下,我们可以在方法上使用new并仍然遵守这个期望,但是一般不应该使用new` 来违反它。 - supercat

-1

我的理解是,其中一个主要动机与库的版本控制有关。

1月1日: X公司发布Dad类作为Super Library v1.0一部分。

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方法标记为new,以区别于Company X的Bar。尽管它们有着相同的名称,但两者和平共存。他们现有的代码将按预期调用他们的Bar,而您现有的代码将按预期调用您的Bar。
class Son : Dad
{
    new public void Bar() { ... };
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接