重写VS方法隐藏

我有点困惑重写与隐藏在C#中的方法。 每个人的实际用途也将被赞赏,以及每个人何时使用的解释。

我很困惑重写 – 为什么我们重写? 到目前为止,我所学到的是,通过重写,我们可以为派生类的方法提供期望的实现,而不改变签名。

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

我也对以下内容感到困惑 – 这是什么certificate?

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? } 

考虑:

 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 

重写是一种经典的OO方式,派生类可以比基类具有更多的特定行为(在某些语言中,你别无select,只能这么做)。 在对象上调用虚拟方法时,将调用该方法的最大派生版本。 因此,即使我们正在处理isReallyDerived作为一个BaseClass然后使用isReallyDerived中定义的function。

隐藏意味着我们有一个完全不同的方法。 当我们在isReallyDerived上调用WriteNum()就没有办法知道isReallyDerived有不同的WriteNum() ,所以不会调用它。 只有当我们将对象作为 DerivedClass处理时才能调用它。

大部分时间隐藏是不好的。 一般来说,如果可能在派生类中更改方法,则应该使用虚方法,并在派生类中重写该方法。 然而,有两件事对于:

  1. 向前兼容。 如果DerivedClass有一个DoStuff()方法,并且稍后BaseClass被更改为添加一个DoStuff()方法(记住它们可能由不同的人写成并存在于不同的程序集中),那么禁止成员隐藏会突然DerivedClass越野车没有改变。 另外,如果BaseClass上的新的DoStuff()是虚拟的,那么在DerivedClass上自动覆盖它就可能导致预先存在的方法被调用。 因此,隐藏是默认的(我们使用new来清除,我们一定要隐藏,但是隐藏并隐藏并在编译时发出警告)。

  2. 穷人的协方差。 考虑一下BaseClass上的Clone()方法,它返回一个新的BaseClass ,它是创build的一个副本。 在DerivedClass的覆盖中,这将创build一个DerivedClass但将其作为BaseClass返回,这不是很有用。 我们可以做的是拥有一个被覆盖的虚拟保护的CreateClone() 。 在BaseClass我们有一个Clone() ,它返回这个结果 – 而且一切都很好 – 在DerivedClass我们用一个新的Clone()来隐藏它,它返回一个DerivedClass 。 在BaseClass上调用Clone()将始终返回一个BaseClass引用,该引用将根据需要成为BaseClass值或DerivedClass值。 在DerivedClass上调用Clone()将返回一个DerivedClass值,这是我们在这种情况下想要的。 这个原则还有其他的变种,但是应该指出,它们都是非常罕见的。

第二种情况需要注意的一个重点是,我们已经使用了隐藏来精确地去除调用代码的意外,因为使用DerivedClass的人可能会合理地期望它的Clone()返回一个DerivedClass 。 任何可以调用的方式的结果都保持一致。 隐藏风险的大多数情况下引入了惊喜,这就是为什么他们普遍皱起了眉头。 这一个是正确的,因为它解决了隐藏经常引入的问题。

总之,隐藏有时是必要的,不常用,但通常是不好的,所以要非常谨慎。

重写是当您在基类中将该方法定义为virtual时在后代类中提供新方法的override实现。

如果在基类中没有将该方法定义为virtual ,或者您的新实现没有指定override ,则在后代类中提供方法的新实现时隐藏。

隐藏往往是不好的; 如果你完全可以避免,你通常应该尽量不要这样做。 隐藏可能会导致意想不到的事情发生,因为隐藏的方法只用于在你定义的实际types的variables上调用,而不是如果使用基类引用…另一方面,被覆盖的虚拟方法将最终即使在子类上使用基类引用进行调用时,也会调用正确的方法版本。

例如,考虑这些类:

 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"); } } 

让我们把这个叫做InheritedClass的实例,在一个匹配的引用中:

 InheritedClass inherited = new InheritedClass(); inherited.Method1(); inherited.Method2(); 

这返回你应该期望的; 两种方法都表示他们正在运行InheritedClass版本。

运行InheritedClass方法1
运行InheritedClass Method2

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

 BaseClass baseRef = new InheritedClass(); baseRef.Method1(); baseRef.Method2(); 

通常,在OOP原则下,您应该会得到与上例相同的输出。 但是你得不到相同的结果:

运行InheritedClass方法1
运行BaseClass Method2

在编写InheritedClass代码时,您可能希望对Method2()所有调用都运行您在其中编写的代码。 通常情况下,这将是它是如何工作的 – 假设你正在使用你已经覆盖的virtual方法。 但是因为您正在使用new /隐藏的方法,所以会调用您正在使用的引用的版本。


如果那是你真正想要的行为,那么; 你去了 但是我强烈build议如果这就是你想要的,代码可能会有更大的架构问题。

方法重载是简单的重写派生类中的基类方法的默认实现。

方法隐藏:您可以在派生类中的虚拟方法之前使用“新”关键字

 class Foo { public virtual void foo1() { } } class Bar:Foo { public new virtual void foo1() { } } 

现在,如果您创build另一个从Bar派生的Bar1类,则可以覆盖Bar中定义的foo1。