题
这个问题在这里已经有答案了:
- C# 中的遮蔽和覆盖之间的区别? 5 个答案
我对覆盖与覆盖有点困惑在 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
重写是经典的面向对象方式,派生类可以比基类具有更具体的行为(在某些语言中,您别无选择,只能这样做)。当对对象调用虚拟方法时,将调用该方法的最派生版本。因此,即使我们正在处理 isReallyDerived
作为一个 BaseClass
然后定义的功能 DerivedClass
用来。
隐藏意味着我们有完全不同的方法。当我们打电话时 WriteNum()
在 isReallyDerived
那么就没有办法知道有不同的 WriteNum()
在 DerivedClass
所以它没有被调用。只有当我们处理对象时才能调用它 作为 A DerivedClass
.
大多数时候隐藏是不好的。一般来说,如果一个方法可能在派生类中更改,则应该将其设置为虚拟方法,然后在派生类中重写它。然而,它有两个用途:
向前兼容性。如果
DerivedClass
有一个DoStuff()
方法,然后再BaseClass
被更改为添加一个DoStuff()
方法,(请记住它们可能是由不同的人编写并存在于不同的程序集中),那么就会突然禁止成员隐藏DerivedClass
没有改变的越野车。另外,如果新DoStuff()
在BaseClass
是虚拟的,然后自动将其打开DerivedClass
对其进行重写可能会导致在不应该调用的情况下调用预先存在的方法。因此,最好隐藏是默认的(我们使用new
为了明确起见,我们肯定想要隐藏,但是将其排除在外会隐藏并在编译时发出警告)。穷人的协方差。考虑一个
Clone()
方法上BaseClass
返回一个新的BaseClass
这是创建的副本。在覆盖上DerivedClass
这将创建一个DerivedClass
但将其返回为BaseClass
, ,这没那么有用。我们能做的就是拥有一个虚拟的保护CreateClone()
那被覆盖了。在BaseClass
我们有一个Clone()
返回结果 - 一切都很好 -DerivedClass
我们用一个新的隐藏它Clone()
返回一个DerivedClass
. 。呼唤Clone()
在BaseClass
总是会返回一个BaseClass
参考,这将是一个BaseClass
值或一个DerivedClass
适当的值。呼唤Clone()
在DerivedClass
将返回一个DerivedClass
值,这就是我们在这种情况下想要的。这一原则还有其他变体,但值得注意的是,它们都非常罕见。
对于第二种情况,需要注意的重要一点是,我们使用隐藏来精确地 消除 调用代码令人惊讶,因为使用的人 DerivedClass
可能合理地预期其 Clone()
返回一个 DerivedClass
. 。任何调用方式的结果都保持一致。大多数隐藏的情况都会带来意外的风险,这就是为什么它们通常不受欢迎的原因。正是因为它解决了隐藏经常带来的问题,所以这一点是合理的。
总而言之,隐藏有时是必要的,虽然很少有用,但通常是不好的,所以要非常警惕。
其他提示
覆盖是当你提供一个新的 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");
}
}
让我们在匹配的引用中使用 InheritedClass 的实例这样调用它:
InheritedClass inherited = new InheritedClass();
inherited.Method1();
inherited.Method2();
这将返回您应该期望的内容;这两种方法都表示它们正在运行 InheritedClass 版本。
运行 InheritedClass 方法 1
运行 InheritedClass 方法2
此代码创建相同 InheritedClass 的实例,但将其存储在 BaseClass 引用中:
BaseClass baseRef = new InheritedClass();
baseRef.Method1();
baseRef.Method2();
通常,根据 OOP 原则,您应该期望得到与上面示例相同的输出。但你没有得到相同的输出:
运行 InheritedClass 方法 1
运行基类方法2
当您编写 InheritedClass 代码时,您可能希望所有调用 Method2()
运行您在其中编写的代码。通常情况下,这就是它的工作方式 - 假设您正在使用 virtual
您已重写的方法。但因为你正在使用 new
/hidden 方法,它会调用您正在使用的引用上的版本。
如果这就是你的行为 真正想要, , 然后;就这样吧。但我强烈建议,如果这就是您想要的,那么代码可能存在更大的架构问题。
方法重写是简单地重写派生类中基类方法的默认实现。
方法隐藏:您可以在派生类中的虚拟方法之前使用“new”关键字
作为
class Foo
{
public virtual void foo1()
{
}
}
class Bar:Foo
{
public new virtual void foo1()
{
}
}
现在,如果您创建另一个从 Bar 派生的类 Bar1 ,您可以覆盖 Bar 中定义的 foo1 。