【问题标题】:Difference between new and override新建和覆盖之间的区别
【发布时间】:2010-11-26 19:32:36
【问题描述】:

想知道以下之间有什么区别:

案例 1:基类

public void DoIt();

案例一:继承类

public new void DoIt();

案例 2:基类

public virtual void DoIt();

案例2:继承类

public override void DoIt();

根据我运行的测试,案例 1 和 2 似乎具有相同的效果。有区别,还是首选方式?

【问题讨论】:

标签: c# inheritance overriding new-operator


【解决方案1】:

覆盖修饰符可用于 虚拟方法,必须用于 抽象方法。这表明对于 编译器使用最后定义的 一种方法的实现。即使 该方法在引用上调用 它将使用的基类 实现覆盖它。

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

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

Base b = new Derived();
b.DoIt();                      // Calls Derived.DoIt

如果覆盖Base.DoIt,将调用Derived.DoIt

新的修饰符指示 编译器使用您的子类实现 而不是父类 执行。任何不是的代码 引用你的班级但父母 类将使用父类 实施。

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

public class Derived : Base
{
    public new void DoIt()
    {
    }
}

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

b.DoIt();                      // Calls Base.DoIt
d.DoIt();                      // Calls Derived.DoIt

将首先拨打Base.DoIt,然后拨打Derived.DoIt。它们实际上是两个完全独立的方法,它们恰好具有相同的名称,而不是派生方法覆盖基方法。

来源:Microsoft blog

【讨论】:

  • This indicates for the compiler to use the last defined implementation of a method。如何找到方法的最后定义实现??
  • 从一个具体的类开始,检查它是否有感兴趣的方法的实现。如果是这样,你就完成了。如果没有,请在继承层次结构中上一层,即检查超类是否具有感兴趣的方法。继续直到找到感兴趣的方法。
  • 另请注意,当基类将方法定义为virtual 时,您只能override 方法。 virtual 这个词是基类说“嘿,当我调用这个方法时,它实际上可能被派生实现所取代,所以我事先并不知道我在运行时实际调用的是什么方法实现。所以virtual 表示方法的占位符。这意味着不能覆盖未标记为virtual 的方法。但是您可以替换派生类中的任何非虚拟方法修饰符new,只能在派生级别访问。
  • 如果你在 Derived 类中直接调用 base.DoIt() 会怎样?
【解决方案2】:

virtual:表示方法可以被继承者覆盖

覆盖:覆盖基类中虚方法的功能,提供不同的功能。

隐藏原始方法(不必是虚拟的),提供不同的功能。这应该只在绝对必要的情况下使用。

当你隐藏一个方法时,你仍然可以通过向上转换到基类来访问原始方法。这在某些情况下很有用,但很危险。

【讨论】:

  • 为什么向上转换隐藏基本方法的方法很危险?或者你是在暗示一般来说向上投射是危险的?
  • @Mark - 调用者可能不知道实现,导致意外误用。
  • 您可以在父方法上使用override 和/或new 而不使用virtual 吗?
  • @Mark:因为如果将其转换为基本类型,它会使用隐藏方法,而不是“新”方法。如果您显式实现接口,肯定会发生这种情况。
【解决方案3】:

在第一种情况下,您将定义隐藏在父类中。这意味着只有当您将对象作为子类处理时才会调用它。如果将类转换为其父类型,则会调用父方法。在第二个实例中,该方法被覆盖,并且无论对象被转换为子类还是父类都会被调用。

【讨论】:

    【解决方案4】:
    • new 表示尊重您的 REFERENCE 类型(= 的左侧),从而运行引用类型的方法。如果重新定义的方法没有 new 关键字,它的行为与它一样。此外,它也被称为非多态继承。那是, “我正在派生类中创建一个全新的方法,该方法具有 绝对与基础中同名的任何方法无关 班级。” - 惠特克说
    • override,必须在其基类中与 virtual 关键字一起使用,表示尊重您的 OBJECT 类型(= 的右侧),从而 无论引用类型如何,都将覆盖运行方法。此外,它也被称为多态继承

    记住这两个关键字彼此相反的方式。

    override:必须定义virtual 关键字来覆盖该方法。使用override关键字的方法,不管引用类型(基类的引用还是派生类的引用),如果用基类实例化,基类的方法就会运行。否则,派生类的方法运行。

    new:如果关键字被方法使用,与override关键字不同,引用类型很重要。如果它是用派生类实例化的,并且引用类型是基类,则运行基类的方法。如果它是用派生类实例化的,并且引用类型是派生类,则派生类的方法运行。即是override关键字的对比。顺便说一句,如果您忘记或忽略在方法中添加新关键字,编译器默认使用new 关键字。

    class A 
    {
        public string Foo() 
        {
            return "A";
        }
    
        public virtual string Test()
        {
            return "base test";
        }
    }
    
    class B: A
    {
        public new string Foo() 
        {
            return "B";
        }
    }
    
    class C: B 
    {
        public string Foo() 
        {
            return "C";
        }
    
        public override string Test() {
            return "derived test";
        }
    }
    

    调用main:

    A AClass = new B();
    Console.WriteLine(AClass.Foo());
    B BClass = new B();
    Console.WriteLine(BClass.Foo());
    B BClassWithC = new C();
    Console.WriteLine(BClassWithC.Foo());
    
    Console.WriteLine(AClass.Test());
    Console.WriteLine(BClassWithC.Test());
    

    输出:

    A
    B
    B
    base test
    derived test
    

    新代码示例,

    通过一个一个注释来玩代码。

    class X
    {
        protected internal /*virtual*/ void Method()
        {
            WriteLine("X");
        }
    }
    class Y : X
    {
        protected internal /*override*/ void Method()
        {
            base.Method();
            WriteLine("Y");
        }
    }
    class Z : Y
    {
        protected internal /*override*/ void Method()
        {
            base.Method();
            WriteLine("Z");
        }
    }
    
    class Programxyz
    {
        private static void Main(string[] args)
        {
            X v = new Z();
            //Y v = new Z();
            //Z v = new Z();
            v.Method();
    }
    

    【讨论】:

    • "override 是相对于赋值的右手,new 是相对于赋值的左手"。记住这一点的好方法,谢谢!
    【解决方案5】:

    尝试以下操作:(case1)

    ((BaseClass)(new InheritedClass())).DoIt()
    

    编辑:virtual+override 在运行时解析(所以 override 确实覆盖了虚拟方法),而 new 只是创建具有相同名称的新方法,并隐藏旧方法,它在编译时解析 -> 你的编译器将调用它“看到”的方法

    【讨论】:

      【解决方案6】:

      如果您在类型被声明为基类时调用了继承类的 DoIt() 方法,则在情况 1 中,您甚至会看到基类的操作。

      /* Results
      Class1
      Base1
      Class2
      Class2
      */
      public abstract class Base1
      {
          public void DoIt() { Console.WriteLine("Base1"); }
      }
      public  class Class1 : Base1 
      {
          public new void DoIt() { Console.WriteLine("Class1"); }
      }
      public abstract class Base2
      {
          public virtual void DoIt() { Console.WriteLine("Base2"); }
      }
      public class Class2 : Base2
      {
          public override void DoIt() { Console.WriteLine("Class2"); }
      }
      static void Main(string[] args)
      {
          var c1 = new Class1();
          c1.DoIt();
          ((Base1)c1).DoIt();
      
          var c2 = new Class2();
          c2.DoIt();
          ((Base2)c2).DoIt();
          Console.Read();
      }
      

      【讨论】:

      • 您能否发布您收到的警告或错误信息。这段代码在我最初发布时运行良好。
      • 这应该全部粘贴到您的入口点类(程序)中。已将其删除以允许在此站点上进行更好的格式设置。
      【解决方案7】:

      这两种情况的区别在于,在情况 1 中,基本的 DoIt 方法不会被覆盖,只是被隐藏了。这意味着取决于变量的类型取决于将调用哪个方法。例如:

      BaseClass instance1 = new SubClass();
      instance1.DoIt(); // Calls base class DoIt method
      
      SubClass instance2 = new SubClass();
      instance2.DoIt(); // Calls sub class DoIt method
      

      这可能真的很令人困惑,并导致非预期行为,应尽可能避免。所以首选的方法是案例 2。

      【讨论】:

        【解决方案8】:

        我有同样的问题,这真的很令人困惑, 您应该考虑 overridenew 关键字仅适用于基类类型的对象和派生类的值。在这种情况下,只有你会看到 override 和 new 的效果: 所以如果你有class ABB继承自A,那么你实例化一个像这样的对象:

        A a = new B();
        

        现在调用方法时会考虑其状态。 Override:表示扩展方法的功能,然后使用派生类中的方法,而new告诉编译器隐藏派生类中的方法,改用基类中的方法。 这是该主题的一个很好的景象:

        https://msdn.microsoft.com/EN-US/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396

        【讨论】:

          【解决方案9】:

          下面的文章在 vb.net,但我认为关于 new vs overrides 的解释很容易掌握。

          https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides

          在文章的某个地方,有这样一句话:

          一般来说,Shadows 假设与类型相关的函数是 被调用,而 Overrides 假定对象实现是 执行。

          这个问题的公认答案是完美的,但我认为这篇文章提供了很好的例子,可以更好地说明这两个关键字之间的差异。

          【讨论】:

            【解决方案10】:

            如果在派生类中使用关键字override,则它会覆盖父方法。

            如果在派生类中使用关键字new,则派生方法被父方法隐藏。

            【讨论】:

              【解决方案11】:

              在所有这些中,是最令人困惑的。通过实验,new 关键字就像是让开发人员可以选择通过显式定义类型来使用基类实现覆盖继承类实现。这就像反过来思考。

              在下面的示例中,结果将返回“Derived result”,直到类型明确定义为 BaseClass 测试,才会返回“Base result”。

              class Program
              {
                  static void Main(string[] args)
                  {
                      var test = new DerivedClass();
                      var result = test.DoSomething();
                  }
              }
              
              class BaseClass
              {
                  public virtual string DoSomething()
                  {
                      return "Base result";
                  }
              }
              
              class DerivedClass : BaseClass
              {
                  public new string DoSomething()
                  {
                      return "Derived result";
                  }
              }
              

              【讨论】:

                【解决方案12】:

                none、virtual、override、new 和 abstract 的所有组合:

                【讨论】:

                  【解决方案13】:

                  这些测试中不会显示功能差异:

                  BaseClass bc = new BaseClass();
                  
                  bc.DoIt();
                  
                  DerivedClass dc = new DerivedClass();
                  
                  dc.ShowIt();
                  

                  在这个例子中,被调用的 Doit 就是你期望被调用的那个。

                  为了看到差异,您必须这样做:

                  BaseClass obj = new DerivedClass();
                  
                  obj.DoIt();
                  

                  如果您运行该测试,您将看到在案例 1(如您定义的那样)中,BaseClass 中的 DoIt() 被调用,在案例 2(如您所定义的那样)中,DoIt() 在 @ 987654326@ 被调用。

                  【讨论】:

                    【解决方案14】:

                    在第一种情况下,它会调用派生类的 DoIt() 方法,因为 new 关键字隐藏了基类的 DoIt() 方法。

                    在第二种情况下,它将调用覆盖的 DoIt()

                      public class A
                    {
                        public virtual void DoIt()
                        {
                            Console.WriteLine("A::DoIt()");
                        }
                    }
                    
                    public class B : A
                    {
                        new public void DoIt()
                        {
                            Console.WriteLine("B::DoIt()");
                        }
                    }
                    
                    public class C : A
                    {
                        public override void DoIt()
                        {
                            Console.WriteLine("C::DoIt()");
                        }
                    }
                    

                    让我们创建这些类的实例

                       A instanceA = new A();
                    
                        B instanceB = new B();
                        C instanceC = new C();
                    
                        instanceA.DoIt(); //A::DoIt()
                        instanceB.DoIt(); //B::DoIt()
                        instanceC.DoIt(); //B::DoIt()
                    

                    一切都在上面。让 instanceB 和 instanceC 为 instanceA 并调用 DoIt() 方法并检查结果。

                        instanceA = instanceB;
                        instanceA.DoIt(); //A::DoIt() calls DoIt method in class A
                    
                        instanceA = instanceC;
                        instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C
                    

                    【讨论】:

                    • instanceC.DoIt();会给你 C::DoIt(),而不是 B::DoIt()
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-06-01
                    • 2012-06-18
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-02-13
                    相关资源
                    最近更新 更多