簡體   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