我来自java背景,目前正在学习c#。我只是对对象从基类/派生类访问方法的方式上的差异感到非常惊讶。我的意思是:
如果我这样做的话
class InheritanceTesting
{
public void InheritanceOne()
{
System.out.println("InheritanceOne");
}
}
class NewInherit extends InheritanceTesting
{
public void InheritanceOne()
{
System.out.println("InheritanceTwo");
}
}
然后运行以下操作:
public static void main(String[] args) {
InheritanceTesting inh = new NewInherit();
inh.InheritanceOne();
}
我得到的结果是:
InheritanceTwo
如果我在c中也这么做:
class InheritanceTesting
{
public void InheritanceOne()
{
Console.WriteLine("InheritanceOne");
}
}
class NewInherit : InheritanceTesting
{
public new void InheritanceOne()
{
Console.WriteLine("InheritanceTwo");
}
}
然后:
InheritanceTesting inh = new NewInherit();
inh.InheritanceOne();
结果是
InheritanceOne
我记得有人用java教我“object知道它被示例化为什么类型”,因此,当我调用重写的方法时,毫不奇怪。这是否意味着情况与c#相反?对象只“知道”其声明的类型?如果是这样的话,这其中的逻辑/优势是什么?在我看来,java将基类视为接口—这是您的类型,这是您的实际实现。我刚到c#也许我错过了一些明显的东西?
5条答案
按热度按时间w1e3prcc1#
下面是一个稍微有趣的例子
您所看到的是c#和java之间的一些差异,您可以让c#像方法inheritanceb所示的java一样工作。
默认情况下,c#方法是final的,因此您需要采取积极的措施,通过将方法标记为virtual来重写该方法。因此,inheratanceb中的虚拟方法的行为将与您期望的方法的行为一样,方法调度基于对象类型,而不是引用类型。例如
都会产生
InheritanceB - Two
因为方法inheritanceb是虚拟的(可以被重写)并且被重写(使用override方法)。你看到的是所谓的方法隐藏,方法不能被重写(非虚),但可以被隐藏,隐藏的方法只有在引用(不是对象)是派生类型时才被隐藏
将产生
InheritanceB - Two
第一个和InheritanceB - One
第二。这是因为(至少在简单的情况下)final方法的调用是在编译时基于引用类型绑定的。这对表演有好处。虚拟方法的绑定需要与运行时不同,因为编译器可能不知道instances类。在实践中,方法隐藏并没有得到广泛的应用,一些组织有禁止它的编码标准。通常的做法是将期望子类能够重写的方法标记为
virtual
在子类中使用关键字override
.更直接地回答你的问题
这是否意味着情况与c#相反?对象只“知道”其声明的类型?
不,c#知道构造的类型(示例)和声明的类型(引用)。它对重写的方法使用示例类型,对final方法使用声明的类型,即使示例已隐藏该方法。
如果是这样的话,这其中的逻辑/优势是什么?
不是这样的,我相信在编译时绑定在性能上是有好处的,比如允许方法的内联。另外,正如所解释的,使用
virtual
以及override
关键词。hfsqlsce2#
你可能会认为你写了同样的东西(同样的词),虽然你没有(你用过)
new
关键字),但c#等价于您在java中编写的内容是这样的。在java中,默认情况下除了
private
s和static
当然,是的virtual
任何一个方法和一个超类方法有相同的签名都是一个override
.vsaztqbk3#
默认情况下,java中的每个方法都可以被其子类重写(除非private/static等)。
在c#中,如果方法必须被子类重写,则必须使其成为虚拟的。
在您的c#示例中,它不是重写的方法,因此行为是预期的。
wb1gzix04#
默认情况下,java使方法成为虚拟的,而在c中,必须显式地启用虚拟继承。
所以你加了
new
是吗?因为你得到了警告?这是因为在没有虚方法的情况下,如果在派生类中重新定义方法,则静态替换方法。如果不是打电话InheritanceOne()
通过一个基指针,您可以通过一个派生指针来调用它,您将得到预期的结果——编译器在编译时仅基于编译时的信息来选择非虚方法。热释光;dr:任何时候你想对一个方法使用继承,就在c#中使它成为虚拟的。
new
对于方法来说,这是语言中最糟糕的事情之一,它没有真正的用途,只会给代码增加缺陷。dxpyg8gm5#
java默认情况下将方法视为虚拟的,而c方法默认情况下是非虚拟的。如果你想在c中有同样的行为,请使用virtual关键字。在java中,可以使用final来确保继承的类不会重写方法。
c#方法在默认情况下不是虚拟的原因很可能是为了防止人们以基类设计器不希望的方式动态更改每个继承函数的行为。这为基类设计器提供了更多的控制,并确保继承是经过仔细和有意规划的,而不是仅仅在动态中完成。