带有继承的c#方法重载

本文关键字:方法 重载 继承 | 更新日期: 2023-09-27 17:52:33

我一直认为c#在运行时通过查看方法调用接收者的运行时类型(即点之前的对象)来动态解析方法调用。

但是,下面的代码示例的工作方式不同。如果我在代码中使用GenericSpaceShip,它返回"Generic";如果我使用太空船,它返回"Specific"。注意,这两种情况下的运行时类型都是太空船。

所以我的问题是:c#如何解决访问方法调用,为什么它看编译时间,而不是运行时类型在这种情况下?

注意两个Visit方法有不同的参数。正如Patko指出的,这意味着我不能在这里使用virtual/override。

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#方法重载

如果你想要真正的动态分辨率,那么你必须使用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       
}

如果使用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;如果有必要,你可以强制转换它)。
  • 你需要使用关键字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)中保留两个具有不同签名的方法。

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