我对在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? }
考虑:
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方法,在这种方法中,派生类可以具有比基类更具体的行为(在某些语言中,您别无选择,只能这样做)。在对象上调用虚拟方法时,将调用该方法的最新派生版本。因此,即使我们正在处理isReallyDerived的BaseClass定义,然后功能DerivedClass被使用。
isReallyDerived
BaseClass
DerivedClass
隐藏意味着我们拥有完全不同的方法。当我们调用时WriteNum(),isReallyDerived就无法知道存在不同之处WriteNum(),DerivedClass因此不会被调用。当我们正在处理的对象时,它只能被称为 是 一个DerivedClass。
WriteNum()
大多数时候,隐藏是不好的。通常,如果某个方法可能会在派生类中更改,则应该将其设为虚拟方法,然后在派生类中对其进行覆盖。但是,有两点有用:
前向兼容性。如果DerivedClass有一个DoStuff()方法,然后又BaseClass更改为添加一个DoStuff()方法,(请记住它们可能是由不同的人编写的,并且存在于不同的程序集中),则禁止成员隐藏的操作可能会突然变成DerivedClass没有更改的错误。另外,如果新的DoStuff()on BaseClass是虚拟的,则在对其DerivedClass进行覆盖时自动进行设置可能会导致在不应该调用现有方法的情况下对其进行调用。因此,默认为隐藏是很好的(我们用来new明确表示我们肯定要隐藏,但是将其保留为隐藏并在编译时发出警告)。
DoStuff()
new
穷人的协方差。考虑一个返回新值的Clone()方法,BaseClass该方法BaseClass是所创建方法的副本。在覆盖上,DerivedClass这将创建一个,DerivedClass但将其返回为BaseClass,这没有什么用处。我们可以做的是拥有一个CreateClone()被覆盖的虚拟保护。在BaseClass我们有一个Clone()返回此结果的-并且一切都很好- DerivedClass我们用一个新的Clone()返回a的隐藏它DerivedClass。调用Clone()上BaseClass总是会返回一个BaseClass参考,这将是一个BaseClass价值或DerivedClass价值合适。调用Clone()上DerivedClass会返回一个DerivedClass值,这是我们在这种情况下想要的。此原理还有其他变体,但是应该注意,它们都很少见。
Clone()
CreateClone()
在第二种情况下要注意的重要一点是,我们已经使用隐藏精确地 消除 了调用代码的意外之处,因为使用该代码的人DerivedClass可能会合理地期望其Clone()返回DerivedClass。可以用任何方式调用的结果都保持一致。大多数隐藏风险的案例都会带来惊喜,这就是为什么人们通常对此不以为然。这样做是正当的,因为它解决了隐藏经常引入的问题。
总而言之,隐藏有时是必需的,很少有用,但通常是不好的,因此要非常警惕。