【问题标题】:C# - Keyword usage virtual+override vs. newC# - 关键字使用 virtual+override 与 new
【发布时间】:2019-07-26 03:23:08
【问题描述】:

在基类型“virtual”中声明方法,然后在子类型中使用“override”关键字覆盖它与在声明子类型中的匹配方法?

【问题讨论】:

标签: c# syntax overriding method-hiding member-hiding


【解决方案1】:

“new”关键字不会覆盖,它表示一个与基类方法无关的新方法。

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

public class Test
{
    public static void Main ()
    {
        Foo test = new Bar ();
        Console.WriteLine (test.DoSomething ());
    }
}

这将打印 false,如果您使用覆盖,它将打印 true。

(取自 Joseph Daigle 的基本代码)

所以,如果你在做真正的多态,你应该总是覆盖。唯一需要使用“new”的地方是方法与基类版本没有任何关系。

【讨论】:

  • 您应该修改您的代码,我已将方法设为静态(这对于使用“new”关键字有效)。但我认为使用实例方法更清晰。
  • 谢谢,我错过了“静态”部分。应该更加关注未来
  • 请注意,Foo test = new Bar () 行在这里至关重要,metods 的 new / override 关键字决定了当您将 Bar 放入 Foo 变量时调用哪个方法。
  • ... 所以它与简单地 not 在基础中具有virtual 并在派生中具有override 完全相同?它为什么存在?即使没有new,代码仍然可以运行 - 所以它纯粹只是可读性吗?
  • 亲爱的先生,您的回答很模糊。 new 和 virtual-override 做同样的事情,唯一的区别是 new 隐藏父类中的方法并覆盖.. 好吧,覆盖它。当然它会打印 false,因为您的测试对象是 Foo 类型,如果它是 Bar 类型,它会打印 true。不过,这个例子很棘手。
【解决方案2】:

new 关键字实际上创建了一个仅存在于该特定类型上的全新成员。

例如

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

该方法存在于两种类型上。当您使用反射并获取类型为Bar 的成员时,您实际上会发现两个名为DoSomething() 的方法看起来完全一样。通过使用new,您可以有效地将实现隐藏在基类中,这样当类派生自Bar(在我的示例中)时,对base.DoSomething() 的方法调用会转到Bar 而不是Foo

【讨论】:

    【解决方案3】:

    override关键字和new关键字的区别在于前者做方法覆盖,后者做方法隐藏。

    查看以下链接了解更多信息...

    MSDNOther

    【讨论】:

      【解决方案4】:

      这里有一些代码来理解虚拟和非虚拟方法的行为差异:

      class A
      {
          public void foo()
          {
              Console.WriteLine("A::foo()");
          }
          public virtual void bar()
          {
              Console.WriteLine("A::bar()");
          }
      }
      
      class B : A
      {
          public new void foo()
          {
              Console.WriteLine("B::foo()");
          }
          public override void bar()
          {
              Console.WriteLine("B::bar()");
          }
      }
      
      class Program
      {
          static int Main(string[] args)
          {
              B b = new B();
              A a = b;
              a.foo(); // Prints A::foo
              b.foo(); // Prints B::foo
              a.bar(); // Prints B::bar
              b.bar(); // Prints B::bar
              return 0;
          }
      }
      

      【讨论】:

      • 谢谢你 - 但是为什么要使用 new 来“隐藏”基本方法,而根本不使用 override 似乎也能做到这一点?
      • 我不认为它是为了防止基类被覆盖而创建的。我认为创建它是为了避免名称冲突,因为您不能覆盖不是 virtual 的方法,如果编译器在没有 virtual 签名的类“血统”上看到相同的函数名称,编译器会抱怨
      • 这是一个非常好的答案。所以new 基本上允许父方法保持可访问性(当孩子被强制转换/用作父类型时)。相反,virtual+override 保证子方法被使用,而不管子如何被强制转换/使用(当然只有在覆盖时)。
      【解决方案5】:

      除了技术细节之外,我认为使用虚拟/覆盖可以在设计上传达大量语义信息。当您声明一个虚拟方法时,您表明您希望实现类可能希望提供它们自己的非默认实现。同样,在基类中省略它,声明了默认方法应该足以满足所有实现类的期望。类似地,可以使用抽象声明来强制实现类提供它们自己的实现。同样,我认为这传达了很多关于程序员期望如何使用代码的信息。如果我同时编写基类和实现类并且发现自己使用 new 我会认真重新考虑不在父类中使方法为虚拟的决定并明确声明我的意图。

      【讨论】:

      • new vs override 的技术解释是合理的,但这个答案似乎最有助于指导开发人员使用。
      【解决方案6】:

      virtual / override 告诉编译器这两个方法是相关的,并且在某些情况下,当您认为您正在调用第一个(虚拟)方法时,调用第二个(覆盖)实际上是正确的代替方法。这是多态的基础。

      (new SubClass() as BaseClass).VirtualFoo()
      

      将调用子类的重写 VirtualFoo() 方法。

      new 告诉编译器您正在向派生类添加一个与基类中的方法同名的方法,但它们彼此没有关系。

      (new SubClass() as BaseClass).NewBar()
      

      将调用 BaseClass 的 NewBar() 方法,而:

      (new SubClass()).NewBar()
      

      将调用子类的 NewBar() 方法。

      【讨论】:

        【解决方案7】:

        我总是觉得用图片更容易理解这样的事情:

        再次获取 joseph daigle 的代码,

        public class Foo
        {
             public /*virtual*/ bool DoSomething() { return false; }
        }
        
        public class Bar : Foo
        {
             public /*override or new*/ bool DoSomething() { return true; }
        }
        

        如果你再这样调用代码:

        Foo a = new Bar();
        a.DoSomething();
        

        注意:重要的是我们的对象实际上是一个Bar,但我们将它存储在Foo类型的变量中(这类似于强制转换它)

        那么结果会如下,具体取决于你在声明类时是使用virtual/override还是new

        【讨论】:

        • 谢谢....但是你能解释一下上面的图片关于你所说的演员吗?
        • 糟糕,如果忘记将这几行添加到我的上一个。评论:您的意思是虚拟/覆盖和非虚拟/新仅用于多态概念,当您简单地声明一个变量(不使用强制转换)时,它们并不意味着?再次感谢。
        【解决方案8】:
        • new 关键字用于隐藏。 - 表示您在运行时隐藏您的方法。输出将基于基类方法。
        • override 用于覆盖。 - 表示您正在使用基类的引用调用派生类方法。输出将基于派生类方法。

        【讨论】:

          【解决方案9】:

          我的解释来自于使用 properties 来帮助理解差异。

          override 很简单,对吧?基础类型覆盖父类型。

          new 可能是误导(对我来说是这样)。使用属性更容易理解:

          public class Foo
          {
              public bool GetSomething => false;
          }
          
          public class Bar : Foo
          {
              public new bool GetSomething => true;
          }
          
          public static void Main(string[] args)
          {
              Foo foo = new Bar();
              Console.WriteLine(foo.GetSomething);
          
              Bar bar = new Bar();
              Console.WriteLine(bar.GetSomething);
          }
          

          使用调试器,您会注意到 Foo foo2 GetSomething 属性,因为它实际上有两个版本的属性,FooBar,以及要知道使用哪一个,c#“选择”当前类型的属性。

          如果您想使用 Bar 的版本,您应该使用 override 或改用 Foo foo

          Bar bar 只有 1,因为它想要 GetSomething 的完全行为。

          【讨论】:

            【解决方案10】:

            不使用任何东西标记方法意味着:使用对象的编译类型绑定此方法,而不是运行时类型(静态绑定)。

            virtual 标记方法意味着:使用对象的运行时类型绑定此方法,而不是编译时类型(动态绑定)。

            在派生类中用override 标记基类virtual 方法意味着:这是使用对象的运行时类型绑定的方法(动态绑定)。

            在派生类中用new标记基类virtual方法意味着:这是一个新方法,与基类中的同名方法没有关系,应该使用对象的编译时间绑定类型(静态绑定)。

            派生类中没有标记基类virtual方法意味着:这个方法被标记为new(静态绑定)。

            标记一个方法abstract的意思是:这个方法是虚拟的,但我不会为它声明一个主体,它的类也是抽象的(动态绑定)。

            【讨论】:

              【解决方案11】:
              using System;  
              using System.Text;  
                
              namespace OverrideAndNew  
              {  
                  class Program  
                  {  
                      static void Main(string[] args)  
                      {  
                          BaseClass bc = new BaseClass();  
                          DerivedClass dc = new DerivedClass();  
                          BaseClass bcdc = new DerivedClass();  
                
                          // The following two calls do what you would expect. They call  
                          // the methods that are defined in BaseClass.  
                          bc.Method1();  
                          bc.Method2();  
                          // Output:  
                          // Base - Method1  
                          // Base - Method2  
                
                          // The following two calls do what you would expect. They call  
                          // the methods that are defined in DerivedClass.  
                          dc.Method1();  
                          dc.Method2();  
                          // Output:  
                          // Derived - Method1  
                          // Derived - Method2  
                
                          // The following two calls produce different results, depending
                          // on whether override (Method1) or new (Method2) is used.  
                          bcdc.Method1();  
                          bcdc.Method2();  
                          // Output:  
                          // Derived - Method1  
                          // Base - Method2  
                      }  
                  }  
                
                  class BaseClass  
                  {  
                      public virtual void Method1()  
                      {  
                          Console.WriteLine("Base - Method1");  
                      }  
                
                      public virtual void Method2()  
                      {  
                          Console.WriteLine("Base - Method2");  
                      }  
                  }  
                
                  class DerivedClass : BaseClass  
                  {  
                      public override void Method1()  
                      {  
                          Console.WriteLine("Derived - Method1");  
                      }  
                
                      public new void Method2()  
                      {  
                          Console.WriteLine("Derived - Method2");  
                      }  
                  }  
              }  
              

              【讨论】:

                猜你喜欢
                • 2011-09-03
                • 2011-09-28
                • 2017-02-17
                • 1970-01-01
                • 2012-10-21
                相关资源
                最近更新 更多