【问题标题】:Why would you want to hide a method using `new`? [duplicate]为什么要隐藏使用`new`的方法? [复制]
【发布时间】:2011-11-03 07:11:47
【问题描述】:

可能重复:
C# - new keyword in method signature

假设我有 3 个班级:GrandDad、Dad、Son。儿子继承父亲,父亲继承祖父。

每个类都实现了 foo。

// GrandDad class: 
public virtual void foo()

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

// Son class: 
public override void foo()

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

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

谢谢

【问题讨论】:

标签: c# architecture new-operator


【解决方案1】:

希望下面的示例为您提供有关新关键字的想法。这真的取决于功能的要求。

public class Base
{
    public virtual void SomeMethod()
    {
    }
}

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

...

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

如果覆盖 Base.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 。它们实际上是两个完全独立的方法,它们恰好具有相同的名称,而不是覆盖基方法的派生方法。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      这就是您想要这样做的原因之一:

      // 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 - 答案取决于您是儿子还是父亲,但在一个完美的世界,不应该这样)。

      【讨论】:

      • @Downvoter:我期待这种形式的解释可能不太适合每个人,但如果你能解释原因,也许我可以即兴发挥......
      • 如果类设计得当,用 ((BaseClass)derivedClassVariable).DoSomething(whatever)` 替换 derivedClassVariable.DoSomething(whatever) 通常会导致编译错误,或者行为相同。在某些情况下,人们可能会在一种方法上有用地使用new,并且仍然遵守这种期望,但通常不应出于违反它的目的而使用new
      【解决方案4】:

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

      1 月 1 日: X 公司在 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 方法标记为,以将其与 Company X 的 Bar 区分开来。尽管奇怪地同名,但两者和平共存。他们现有的代码将按预期调用他们的 Bar,而您现有的代码将按预期调用您的 Bar。

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

      【讨论】:

        【解决方案5】:

        当您在更多层(Control、Button、RedButton)上具有类层次结构并且您希望限制给定类(Button)的任何子类以更改从 Control 继承的通用行为时,这种情况可能“有用” ,即由Button修改;按 Button 规则播放,而不是按 Control 规则播放,否则 RedButton 可以成为 TreeViewControl :) ...;当您继承实现时可能会发生这种情况:)

        还有其他更简洁的技术,例如“策略模式”(您聚合行为,而不是继承)用于处理之前的场景。

        【讨论】:

          【解决方案6】:

          我将举一个超越“它的作用”的例子,并介绍它什么时候有用。

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

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

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

          public class Person : List<Address> { }
          

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

          1. 不要通过基类型 (List) 引用 Person 类,并且...
          2. 使用“new”关键字隐藏基本列表“添加”操作。

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

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

          public class Node : List<Node> { }
          

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

          【讨论】:

            猜你喜欢
            • 2016-02-13
            • 2011-02-22
            • 1970-01-01
            • 1970-01-01
            • 2015-08-05
            • 1970-01-01
            • 2013-07-27
            • 1970-01-01
            • 2019-03-02
            相关资源
            最近更新 更多