覆盖vs方法隐藏

Nov*_*Net 54 c# overriding method-hiding

关于覆盖与隐藏C#中的方法有点困惑.每个人的实际用途也将被理解,以及何时使用每个人的解释.

我对重写感到困惑 - 为什么要覆盖?到目前为止我所学到的是,通过覆盖,我们可以在不改变签名的情况下为派生类的方法提供所需的实现.

如果我不覆盖超类的方法并且我对子类中的方法进行了更改,那么是否会更改超类方法?

我也对以下内容感到困惑 - 这表明了什么?

class A
{
    virtual m1()
    {
        console.writeline("Bye to all");
    }
}

class B : A
{
    override m1()
    {
        console.writeLine("Hi to all");
    }
}

class C
{
    A a = new A();
    B b = new B();
    a = b; (what is this)
    a.m1(); // what this will print and why?

    b = a; // what happens here?
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*nna 126

考虑:

public class BaseClass
{
  public void WriteNum()
  {
    Console.WriteLine(12);
  }
  public virtual void WriteStr()
  {
    Console.WriteLine("abc");
  }
}

public class DerivedClass : BaseClass
{
  public new void WriteNum()
  {
    Console.WriteLine(42);
  }
  public override void WriteStr()
  {
    Console.WriteLine("xyz");
  }
}
/* ... */
BaseClass isReallyBase = new BaseClass();
BaseClass isReallyDerived = new DerivedClass();
DerivedClass isClearlyDerived = new DerivedClass();

isReallyBase.WriteNum(); // writes 12
isReallyBase.WriteStr(); // writes abc
isReallyDerived.WriteNum(); // writes 12
isReallyDerived.WriteStr(); // writes xyz
isClearlyDerived.WriteNum(); // writes 42
isClearlyDerived.writeStr(); // writes xyz
Run Code Online (Sandbox Code Playgroud)

覆盖是经典的OO方式,其中派生类可以具有比基类更具体的行为(在某些语言中,您别无选择,只能这样做).在对象上调用虚方法时,将调用该方法的派生程度最高的版本.因此,即使我们正在处理使用isReallyDerivedBaseClass定义的功能DerivedClass.

隐藏意味着我们有一个完全不同的方法.当我们打电话时WriteNum(),isReallyDerived没有办法知道有不同的东西WriteNum(),DerivedClass所以它没有被调用.当我们正在处理的对象时,它只能被称为一个DerivedClass.

大部分时间隐藏都很糟糕.通常,如果方法可能在派生类中更改,则应将其作为虚方法,并在派生类中覆盖它.然而,它有两件事是有用的:

  1. 向前兼容性.如果DerivedClass有一个DoStuff()方法,然后BaseClass更改为添加DoStuff()方法,(记住它们可能由不同的人编写并存在于不同的程序集中)那么禁止成员隐藏会突然变成DerivedClass错误而不会改变.此外,如果new DoStuff()on BaseClass是虚拟的,那么在DerivedClass覆盖它时自动进行该操作可能会导致在不应该的情况下调用预先存在的方法.因此隐藏是默认的好(我们用new它来表明我们肯定想要隐藏,但是将它隐藏起来并在编译时发出警告).

  2. 穷人的协方差.考虑一个Clone()方法BaseClass,返回一个新BaseClass的,它是创建的副本.在覆盖DerivedClass上将创建一个DerivedClass但是将其作为a返回BaseClass,这不是很有用.我们可以做的是拥有一个CreateClone()被覆盖的虚拟保护.在BaseClass我们有一个Clone()返回结果 - 并且一切都很好 - DerivedClass我们用一个Clone()返回a 的新隐藏它DerivedClass.调用Clone()BaseClass总是会返回一个BaseClass参考,这将是一个BaseClass价值或DerivedClass价值合适.调用Clone()DerivedClass会返回一个DerivedClass价值,这是我们在这种背景下所需要的.这个原则还有其他变体,但是应该注意它们都非常罕见.

第二种情况需要注意的一件重要事情是,我们已经精确地使用隐藏来消除调用代码的意外,因为使用的人DerivedClass可能会合理地期望它Clone()返回一个DerivedClass.可以调用的任何方式的结果都保持一致.大多数隐藏风险的案例都会引发意外,这就是他们普遍不赞成的原因.这个是正确的,因为它解决了隐藏经常引入的问题.

总之,隐藏有时是必要的,很少有用,但通常很糟糕,所以要非常小心.


And*_*ber 25

压倒一切的是当你提供一个新的override,当该方法在基类中定义为实现在子类中的方法virtual.

隐藏是当你提供了当该方法的新的实现在子类中的方法不是在基类中的定义virtual,或者当你的新的实现并没有指定override.

隐藏往往很糟糕; 你应该尽量不要这样做,如果你可以完全避免它.隐藏可能会导致意想不到的事情发生,因为隐藏方法只用于您所定义的实际类型的变量调用时,如果不使用基类引用...在另一方面,被覆盖的虚拟方法将结束即使在子类上使用基类引用调用时,也会调用正确的方法版本.

例如,考虑以下类:

public class BaseClass
{
  public virtual void Method1()  //Virtual method
  {
    Console.WriteLine("Running BaseClass Method1");
  }
  public void Method2()  //Not a virtual method
  {
    Console.WriteLine("Running BaseClass Method2");
  }
}
public class InheritedClass : BaseClass
{
  public override void Method1()  //Overriding the base virtual method.
  {
    Console.WriteLine("Running InheritedClass Method1");
  }
  public new void Method2()  //Can't override the base method; must 'new' it.
  {
    Console.WriteLine("Running InheritedClass Method2");
  }
}
Run Code Online (Sandbox Code Playgroud)

让我们称呼它,用InheritedClass的一个实例,在匹配的参考:

InheritedClass inherited = new InheritedClass();
inherited.Method1();
inherited.Method2();
Run Code Online (Sandbox Code Playgroud)

这会返回你应该期待的东西; 两种方法都表示它们正在运行InheritedClass版本.

运行InheritedClass方法1
运行InheritedClass Method2

此代码创建相同的实例InheritedClass,但将其存储在BaseClass引用中:

BaseClass baseRef = new InheritedClass();
baseRef.Method1();
baseRef.Method2();
Run Code Online (Sandbox Code Playgroud)

通常,根据OOP原则,您应该期望与上面的示例相同的输出.但是你得不到相同的输出:

运行InheritedClass方法1
运行BaseClass方法2

当您编写InheritedClass代码时,您可能希望所有调用Method2()都运行您在其中编写的代码.通常,这将是它的工作原理 - 假设您正在使用virtual已被覆盖的方法.但是因为您使用的是new/ hidden方法,所以它会调用您正在使用的引用上的版本.


如果这是你真正想要的行为,那么; 你去吧 但我强烈建议如果这就是你想要的,那么代码可能存在更大的架构问题.