繁体   English   中英

带有继承的 C# 方法重载

[英]C# method overload with inheritance

我一直认为 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
    }
}

如果你想要真正的动态分辨率,那么你必须使用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 方法,则无论行星类型如何,都将调用特定方法。 在这种情况下,船舶类型很重要:

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       
}

如果使用GenericSpaceShip ,您将始终获得Generic

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

在 C# 中,如果要在派生类中覆盖它们,则必须明确地将方法声明为虚拟/抽象和覆盖。

所以它应该读

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

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

virtual 和 override 是您要查找的关键字。

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM