带有继承的C#方法重载

问题描述 投票:1回答:5

我一直认为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
    }
}
c# inheritance dispatch
5个回答
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的实例的通用方法,则无论行星的类型如何,都会调用特定方法。在这种情况下,船舶类型很重要:

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


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;如果需要,可以强制转换)。
  • 您需要使用关键字virtualoverride告诉编译器这两种方法是相关的。

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");
    }
}

0
投票

虚拟和替代是您要查找的关键字。

class GenericSpaceShip
{
    public virtual void Visit(GenericPlanet planet)
    {
        Console.WriteLine("Generic");
    }
}

class SpaceShip : GenericSpaceShip
{
    public override void Visit(Planet planet)
    {
        Console.WriteLine("Specific");
    }
}

0
投票

您正在使用继承进行方法重载,这意味着您只在基类(SpaceShip)和派生类(GenericSpaceShip)中保留了两个具有不同签名的方法。

间接派生的类对象将始终具有具有不同签名的这两个方法,这将在编译时进行检查。 您不会覆盖具有相同签名和返回类型的任何方法,因此不会有任何运行时或动态检查。

© www.soinside.com 2019 - 2024. All rights reserved.