重写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
处理时才能调用它。
大部分时间隐藏是不好的。 一般来说,如果可能在派生类中更改方法,则应该使用虚方法,并在派生类中重写该方法。 然而,有两件事对于:
-
向前兼容。 如果
DerivedClass
有一个DoStuff()
方法,并且稍后BaseClass
被更改为添加一个DoStuff()
方法(记住它们可能由不同的人写成并存在于不同的程序集中),那么禁止成员隐藏会突然DerivedClass
越野车没有改变。 另外,如果BaseClass
上的新的DoStuff()
是虚拟的,那么在DerivedClass
上自动覆盖它就可能导致预先存在的方法被调用。 因此,隐藏是默认的(我们使用new
来清除,我们一定要隐藏,但是隐藏并隐藏并在编译时发出警告)。 -
穷人的协方差。 考虑一下
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。