简体   繁体   English

带有继承的 C# 方法重载

[英]C# method overload with inheritance

I always thought C# resolves method calls dynamically during runtime by looking at the runtime type of the method call receiver (ie the object before the dot).我一直认为 C# 是通过查看方法调用接收者的运行时类型(即点之前的对象)来动态解析方法调用的。

However the following code sample works differently.但是,以下代码示例的工作方式不同。 If I use GenericSpaceShip in the code it returns "Generic";如果我在代码中使用 GenericSpaceShip,它会返回“Generic”; if I use SpaceShip it returns "Specific".如果我使用 SpaceShip,它会返回“Specific”。 Note that the runtime type in both cases is SpaceShip.请注意,这两种情况下的运行时类型都是 SpaceShip。

So my question is: How does C# resolve the Visit method call and why does it look at the compile time rather than the runtime type in this situation?所以我的问题是:C# 如何解决 Visit 方法调用,为什么在这种情况下它查看编译时间而不是运行时类型?

Note that the two Visit methods have different parameters.请注意,这两个 Visit 方法具有不同的参数。 As Patko points out, this means I cannot use virtual/override here.正如 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
    }
}

If you want to have really dynamic resolution, then you have to use dynamic keyword, like so:如果你想要真正的动态分辨率,那么你必须使用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
}

In this case behaviour will be something like you have described - type of the parameter matters.在这种情况下,行为将类似于您所描述的 - 参数类型很重要。 But what you most likely want is to have a method override, like so:但是你最有可能想要的是有一个方法覆盖,像这样:

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

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

In this case the Specific method will be called if you have an instance of SpaceShip and Generic method for instance of GenericSpaceShip , regardless of planet type.在这种情况下,如果您有SpaceShip的实例和GenericSpaceShip实例的 Generic 方法,则无论行星类型如何,都将调用特定方法。 In this case type of the ship matters:在这种情况下,船舶类型很重要:

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       
}

You would always get Generic if GenericSpaceShip was used instead.如果使用GenericSpaceShip ,您将始终获得Generic

C# has two ways of doing it: C#有两种实现方式:

  • Resolve at run time (overriding) - this works for abstract and virtual methods, and for methods implementing an interface.在运行时解析(覆盖) - 这适用于抽象和虚拟方法,以及实现接口的方法。 Among other things, this requires that method return types and parameters are the same.除此之外,这要求方法返回类型和参数相同。
  • Resolve at compile time (hiding) - this works for methods that have the same name and arguments, but are not virtual.在编译时解析(隐藏) - 这适用于具有相同名称和参数但不是虚拟的方法。

This gives you maximum control over the method resolution process, but it also requires that you tell the compiler of your decision.这使您可以最大程度地控制方法解析过程,但它也要求您将您的决定告诉编译器。

In your code the method in the derived class hides the method in the base class.在您的代码中,派生类中的方法隐藏了基类中的方法。 Here is how to modify your code to make it an override:以下是修改代码以使其成为覆盖的方法:

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

Note the two things that need to happen:请注意需要发生的两件事:

  • The signature of the overriding method must be the same as that of the base method (hence, both of them take GenericPlanet ; you can cast it if necessary).覆盖方法的签名必须与基方法的签名相同(因此,它们都采用GenericPlanet ;如果需要,您可以GenericPlanet )。
  • You need to use keywords virtual and override to tell the compiler that the two methods are related.您需要使用关键字virtualoverride来告诉编译器这两个方法是相关的。

In C# you have to declare your methods explicitely as virtual/abstract and override if you want to override them in a derived class.在 C# 中,如果要在派生类中覆盖它们,则必须明确地将方法声明为虚拟/抽象和覆盖。

So it should read所以它应该读

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

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

virtual and override are the keywords you're looking for. 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");
    }
}

You are using inheritance for method overloading, which means you are just keeping two methods with different signatures in the base class(SpaceShip) and derived class(GenericSpaceShip).您正在使用继承进行方法重载,这意味着您只是在基类(SpaceShip)和派生类(GenericSpaceShip)中保留两个具有不同签名的方法。

Indirectly derived class objects will always have these two methods with a different signature and, this is going to check at compile time.间接派生类对象将始终具有这两个具有不同签名的方法,这将在编译时进行检查。 You are not overriding any method having the same signature and return type, so there will not be any run-time or dynamic check.您不会覆盖任何具有相同签名和返回类型的方法,因此不会有任何运行时或动态检查。

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

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