我一直以为C#在运行时通过查看方法调用接收器的运行时类型(即点之前的对象)来动态解析方法调用。
但是,以下代码示例的工作方式不同。如果我在代码中使用GenericSpaceShip,则返回“Generic”;如果我使用SpaceShip,则返回“Specific”。请注意,两种情况下的运行时类型都是SpaceShip。
所以我的问题是:C#如何解析Visit方法调用?为什么它会在这种情况下查看编译时而不是运行时类型?
请注意,两种Visit方法具有不同的参数。正如Patko所指出的,这意味着我不能在这里使用虚拟/覆盖。
class GenericSpaceShip
{
public void Visit(GenericPlanet planet)
{
Console.WriteLine("Generic");
}
}
class SpaceShip : GenericSpaceShip
{
public void Visit(Planet planet)
{
Console.WriteLine("Specific");
}
}
class GenericPlanet { }
class Planet : GenericPlanet { }
class Starter
{
static void Main(string[] args)
{
// SpaceShip ship = new SpaceShip();
GenericSpaceShip ship = new SpaceShip();
Planet planet = new Planet();
ship.Visit(planet); // => Generic
}
}
答案 0 :(得分:3)
C#有两种方法:
这使您可以最大程度地控制方法解析过程,但它还要求您告诉编译器您的决定。
在您的代码中,派生类中的方法隐藏了基类中的方法。以下是修改代码以使其成为覆盖的方法:
class GenericSpaceShip {
// Mark the base method virtual
public virtual void Visit(GenericPlanet planet) {
Console.WriteLine("Generic");
}
}
class SpaceShip : GenericSpaceShip {
// Mark the overriding method as such.
// Also note that you cannot change argument types when you override:
public override void Visit(GenericPlanet planet) {
Console.WriteLine("Specific");
}
}
请注意需要做的两件事:
GenericPlanet
;如果需要,可以进行转换。virtual
和override
来告诉编译器这两种方法是相关的。答案 1 :(得分:3)
如果您想拥有真正的动态分辨率,那么您必须使用dynamic
关键字,如下所示:
static void Main(string[] args)
{
dynamic ship = new SpaceShip();
Planet planet = new Planet();
ship.Visit(planet); // => Specific
// also
GenericPlanet genericPlanet = new GenericPlanet();
ship.Visit(planet); // Generic
}
在这种情况下,行为将像您所描述的那样 - 参数的类型很重要。但你最想要的是有一个方法覆盖,如下所示:
class GenericSpaceShip
{
public virtual void Visit(GenericPlanet planet)
{
Console.WriteLine("Generic");
}
}
class SpaceShip : GenericSpaceShip
{
public override void Visit(GenericPlanet planet)
{
Console.WriteLine("Specific");
}
}
在这种情况下,如果您有SpaceShip
的实例和GenericSpaceShip
的Generic方法,则会调用Specific方法,而不管行星类型如何。在这种情况下,船舶的类型很重要:
static void Main(string[] args)
{
SpaceShip ship = new SpaceShip();
Planet planet = new Planet();
ship.Visit(planet); // => Specific
// also
GenericPlanet genericPlanet = new GenericPlanet();
ship.Visit(planet); // Specific
}
如果使用Generic
,您将始终获得GenericSpaceShip
。
答案 2 :(得分:2)
在C#中,你必须明确地将你的方法声明为virtual / abstract,并且如果要在派生类中覆盖它们,则必须覆盖它们。
所以应该阅读
class GenericSpaceShip
{
public virtual void Visit(GenericPlanet planet)
{
Console.WriteLine("Generic");
}
}
class SpaceShip : GenericSpaceShip
{
public override void Visit(Planet planet)
{
Console.WriteLine("Specific");
}
}
答案 3 :(得分:0)
虚拟和覆盖是您正在寻找的关键字。
class GenericSpaceShip
{
public virtual void Visit(GenericPlanet planet)
{
Console.WriteLine("Generic");
}
}
class SpaceShip : GenericSpaceShip
{
public override void Visit(Planet planet)
{
Console.WriteLine("Specific");
}
}
答案 4 :(得分:0)
您正在使用继承进行方法重载,这意味着您仅在基类(SpaceShip)和派生类(GenericSpaceShip)中保留了两个具有不同签名的方法。
间接派生的类对象将始终具有具有不同签名的这两个方法,这将在编译时进行检查。 您不会覆盖具有相同签名和返回类型的任何方法,因此不会进行任何运行时检查或动态检查。