隐藏在 C# 和 Java 中的基本方法
Base method hiding in C# and Java
我来自 Java 背景,目前正在学习 C#。我对(我认为的)对象从 base/derived class 访问方法的方式的差异感到非常惊讶。这就是我的意思:
在 Java 如果我做这样的事情
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 knows what type it is instantiated to",因此,当我调用被覆盖的方法时并不奇怪。这是否意味着在 C# 中情况正好相反?仅对象 "knows" 其声明的类型?如果是这样,那里面的 logic/advantage 是什么?在我看来,Java 将基础 classes 视为接口 - 这是您的类型,这是您的实际实现。我是 C# 的新手,也许我在这里遗漏了一些明显的东西?
Java 默认将方法视为虚拟方法,C# 方法默认为非虚拟方法。如果您希望在 C# 中有相同的行为,请使用 virtual 关键字。在 Java 中,您可以使用 final 来确保继承的 class 不会覆盖方法。
C# 方法在默认情况下不是虚拟的,这很可能是为了防止人们能够以基本 class 设计者不希望的方式即时更改每个继承函数的行为。这为基础 class 设计人员提供了更多控制权,并确保继承是经过仔细和有意计划的,而不是临时完成的。
默认情况下,Java 中的每个方法都可以被其子 类 覆盖(除非 private/static 等)。
在 C# 中,如果方法必须被子方法重写,则必须将其设为虚拟方法 类。
在您的 C# 示例中,它不是重写方法,因此该行为是预期的。
下面是一个稍微有趣一点的案例
class InheritanceTesting
{
public void InheritanceOne()
// Java equivalent would be
// public final void InheritanceA()
{
Console.WriteLine("InheritanceA - One");
}
public virtual void InheritanceB()
// Java equivalent would be
// public void InheritanceB() // note the removing of final
{
Console.WriteLine("InheritanceB - One");
}
}
class NewInherit : InheritanceTesting
{
public new void InheritanceOne()
// There is no Java equivalent to this statement
{
Console.WriteLine("InheritanceA - Two");
}
public override void InheritanceB()
// Java equivalent would be
// public void InheritanceB()
{
Console.WriteLine("InheritanceB - Two");
}
}
您看到的是 C# 和 Java 之间的一些区别,您可以让 C# 的行为类似于 Java,正如方法 InheritanceB 将显示的那样。
默认情况下,C# 方法是最终方法,因此您需要采取积极措施,通过将方法标记为虚拟方法来重写该方法。因此,虚方法 InheratanceB 的行为将与您期望的方法一样,方法分派基于对象类型,而不是引用类型。例如
NewInherit example = new NewInherit();
InheritanceTesting secondReference = example;
example.InheritanceB();
secondreference.InheritanceB();
由于方法 InheritanceB 是虚拟的(能够被覆盖)和被覆盖(使用覆盖方法),因此两者都会产生 InheritanceB - Two
。
你在哪里看到的叫做方法隐藏,其中方法不能被覆盖(非虚拟)但可以隐藏,隐藏方法只有在引用(而不是对象)是派生类型时才会隐藏所以
NewInherit example = new NewInherit();
InheritanceTesting secondReference = example;
example.InheritanceA();
secondreference.InheritanceA();
首先会产生 InheritanceB - Two
,然后会产生 InheritanceB - One
。这是因为(至少在简单的情况下)final 方法的调用是在编译时根据引用类型绑定的。这具有性能优势。虚拟方法的绑定需要不同于运行时,因为编译器可能不知道实例 class.
实际上方法隐藏并没有被广泛使用,一些组织的编码标准禁止它。通常的做法是将您希望 sub-class 能够覆盖的方法标记为 virtual
,并在 sub-class 中使用关键字 override
。
更直接地回答您的问题
Does this mean that the situation is the opposite in C#? Object only
"knows" its declared type?
不,c# 知道构造类型(实例)和声明类型(引用)。即使实例隐藏了方法,它也使用重写方法的实例类型和最终方法的声明类型。
If so, what is the logic/advantage in that?
不是这样,我相信在可能的情况下在编译时绑定会带来性能优势,例如允许内联方法。此外,正如所解释的那样,不会损失灵活性,因为您可以通过使用 virtual
和 override
关键字获得与 Java 相同的行为。
Java 默认使方法成为虚拟方法,而在 C# 中,您必须显式启用虚拟继承。
这就是你添加 new
修饰符的原因吧?因为你收到了警告?那是因为如果方法不是虚拟的,如果在派生的 class 中重新定义它,则可以 静态地 替换它。如果不是通过基指针调用 InheritanceOne()
,而是通过派生指针调用它,您将获得预期的结果——编译器仅根据编译时信息在编译时选择非虚拟方法。
TL;DR:只要您想对方法使用继承,请在 C# 中将其设为虚拟。 new
是语言中最糟糕的方法之一,它没有实际用途,只会给您的代码添加陷阱。
你可能认为你写了同样的东西(相同的词),虽然你没有(你在 c# 版本中使用了 new
关键字)但是 C# 等同于你在 Java 中写的是这个吗
class InheritanceTesting
{
public virtual void InheritanceOne()
{
Console.WriteLine("InheritanceOne");
}
}
class NewInherit : InheritanceTesting
{
public override void InheritanceOne()
{
Console.WriteLine("InheritanceTwo");
}
}
在 Java 中,默认情况下,除了 private
s 和 static
s 之外的所有方法都是 virtual
并且任何方法都具有与超级 [ 相同的签名=22=] 方法是一个 override
.
我来自 Java 背景,目前正在学习 C#。我对(我认为的)对象从 base/derived class 访问方法的方式的差异感到非常惊讶。这就是我的意思:
在 Java 如果我做这样的事情
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 knows what type it is instantiated to",因此,当我调用被覆盖的方法时并不奇怪。这是否意味着在 C# 中情况正好相反?仅对象 "knows" 其声明的类型?如果是这样,那里面的 logic/advantage 是什么?在我看来,Java 将基础 classes 视为接口 - 这是您的类型,这是您的实际实现。我是 C# 的新手,也许我在这里遗漏了一些明显的东西?
Java 默认将方法视为虚拟方法,C# 方法默认为非虚拟方法。如果您希望在 C# 中有相同的行为,请使用 virtual 关键字。在 Java 中,您可以使用 final 来确保继承的 class 不会覆盖方法。
C# 方法在默认情况下不是虚拟的,这很可能是为了防止人们能够以基本 class 设计者不希望的方式即时更改每个继承函数的行为。这为基础 class 设计人员提供了更多控制权,并确保继承是经过仔细和有意计划的,而不是临时完成的。
默认情况下,Java 中的每个方法都可以被其子 类 覆盖(除非 private/static 等)。
在 C# 中,如果方法必须被子方法重写,则必须将其设为虚拟方法 类。
在您的 C# 示例中,它不是重写方法,因此该行为是预期的。
下面是一个稍微有趣一点的案例
class InheritanceTesting
{
public void InheritanceOne()
// Java equivalent would be
// public final void InheritanceA()
{
Console.WriteLine("InheritanceA - One");
}
public virtual void InheritanceB()
// Java equivalent would be
// public void InheritanceB() // note the removing of final
{
Console.WriteLine("InheritanceB - One");
}
}
class NewInherit : InheritanceTesting
{
public new void InheritanceOne()
// There is no Java equivalent to this statement
{
Console.WriteLine("InheritanceA - Two");
}
public override void InheritanceB()
// Java equivalent would be
// public void InheritanceB()
{
Console.WriteLine("InheritanceB - Two");
}
}
您看到的是 C# 和 Java 之间的一些区别,您可以让 C# 的行为类似于 Java,正如方法 InheritanceB 将显示的那样。
默认情况下,C# 方法是最终方法,因此您需要采取积极措施,通过将方法标记为虚拟方法来重写该方法。因此,虚方法 InheratanceB 的行为将与您期望的方法一样,方法分派基于对象类型,而不是引用类型。例如
NewInherit example = new NewInherit();
InheritanceTesting secondReference = example;
example.InheritanceB();
secondreference.InheritanceB();
由于方法 InheritanceB 是虚拟的(能够被覆盖)和被覆盖(使用覆盖方法),因此两者都会产生 InheritanceB - Two
。
你在哪里看到的叫做方法隐藏,其中方法不能被覆盖(非虚拟)但可以隐藏,隐藏方法只有在引用(而不是对象)是派生类型时才会隐藏所以
NewInherit example = new NewInherit();
InheritanceTesting secondReference = example;
example.InheritanceA();
secondreference.InheritanceA();
首先会产生 InheritanceB - Two
,然后会产生 InheritanceB - One
。这是因为(至少在简单的情况下)final 方法的调用是在编译时根据引用类型绑定的。这具有性能优势。虚拟方法的绑定需要不同于运行时,因为编译器可能不知道实例 class.
实际上方法隐藏并没有被广泛使用,一些组织的编码标准禁止它。通常的做法是将您希望 sub-class 能够覆盖的方法标记为 virtual
,并在 sub-class 中使用关键字 override
。
更直接地回答您的问题
Does this mean that the situation is the opposite in C#? Object only "knows" its declared type?
不,c# 知道构造类型(实例)和声明类型(引用)。即使实例隐藏了方法,它也使用重写方法的实例类型和最终方法的声明类型。
If so, what is the logic/advantage in that?
不是这样,我相信在可能的情况下在编译时绑定会带来性能优势,例如允许内联方法。此外,正如所解释的那样,不会损失灵活性,因为您可以通过使用 virtual
和 override
关键字获得与 Java 相同的行为。
Java 默认使方法成为虚拟方法,而在 C# 中,您必须显式启用虚拟继承。
这就是你添加 new
修饰符的原因吧?因为你收到了警告?那是因为如果方法不是虚拟的,如果在派生的 class 中重新定义它,则可以 静态地 替换它。如果不是通过基指针调用 InheritanceOne()
,而是通过派生指针调用它,您将获得预期的结果——编译器仅根据编译时信息在编译时选择非虚拟方法。
TL;DR:只要您想对方法使用继承,请在 C# 中将其设为虚拟。 new
是语言中最糟糕的方法之一,它没有实际用途,只会给您的代码添加陷阱。
你可能认为你写了同样的东西(相同的词),虽然你没有(你在 c# 版本中使用了 new
关键字)但是 C# 等同于你在 Java 中写的是这个吗
class InheritanceTesting
{
public virtual void InheritanceOne()
{
Console.WriteLine("InheritanceOne");
}
}
class NewInherit : InheritanceTesting
{
public override void InheritanceOne()
{
Console.WriteLine("InheritanceTwo");
}
}
在 Java 中,默认情况下,除了 private
s 和 static
s 之外的所有方法都是 virtual
并且任何方法都具有与超级 [ 相同的签名=22=] 方法是一个 override
.