簡體   English   中英

在 C# 中將派生類向上轉換為基類

[英]Upcasting derived class to base class in C#

我正在努力通過鑄造來理解這種情況。 出於演示目的,我創建了這些類:

public interface IFoo {}
public class Foo : IFoo
{
      public string Name { get; set; }
}
public class Bar : Foo
{
      public string Surname { get; set; }
}

現在,在我的 Main 方法中,我創建了一個返回 IFoo 的靜態方法:

class Program 
{
    static void Main()
    {
        IFoo iFoo = GetIFoo();
        Foo foo = (Foo)iFoo;           
       //the GetType ext method is executed and it returns Bar
       if(foo is Foo)
       {
            Console.WriteLine($"foo is of type: {foo.GetType()}");
       }
       Console.ReadLine();
    }
    static IFoo GetIFoo()
    {
         var bar = new Bar
         {
             Name = "MyName",
             Surname = "MySurname"
         }    
         return bar;
    }
}

問題是:即使我在 GetIFoo 方法中創建了一個 Bar,為什么如果我將 IFoo 強制轉換為 Foo,foo 的類型是 Bar 而不是 Foo?

為什么如果我將iFooFoofoo的類型是Bar而不是Foo

這是因為強制轉換改變了靜態類型。 它對動態類型沒有影響。 foo靜態類型是Foo ,但它的動態類型是Bar

每個變量都有一個編譯器已知的類型(靜態類型)和一個運行時已知的類型(動態類型)。 在某些情況下,這兩種類型是相同的,但它們不必一直相同。

靜態類型是變量的聲明類型。 動態類型是在將對象分配給該變量后通過調用GetType獲得的。

當變量的靜態類型與其動態類型不匹配時,您構造了一個示例: iFoo靜態類型為IFoo ,但其動態類型為Bar 這完全沒問題,因為BarIFoo兼容,因為它實現了接口,也與Foo兼容,因為它擴展了類。

這真的是繼承 101 :創建的對象是一個Bar並且強制轉換只會影響您查看它的方式,而不是它的真正含義。

在這里,您將其視為IFoo

IFoo iFoo = GetIFoo();

在這里,您故意將其視為Foo 如果它不是Foo或其任何后代,您會在運行時得到InvalidCastException

Foo foo = (Foo)iFoo;

Bar實例可以合法地作為BarFooIFoo (為了完整起見,也可以作為Object使用)。 無論您如何查看實例,它仍然是一個Bar

Type.GetType為您提供基礎實例的類型(即您在new之后指定的類型),無論它是什么類型的變量。 此外, foo is Foo給人true不是只有在實際的實例是Foo ,但對來源於每一個類型Foo

因為用線:

Foo foo = (Foo) iFoo;

轉換:一個Bar是一個Foo和一個IFoo 因此,當您iFoo(Foo) ,C# 解釋器會注意到iFoo是一個Bar ,因此該對象保持不變。 關鍵是,從現在開始,編譯器將假定foo確實是一個Foo對象,但它可以是擴展Foo類的任何類。

讓我們通過以下示例更清楚地說明:

interface IDriveable {}  // IFoo
class Car : IDriveable { // Foo
    string Name {get; set; }
}
class Volkswagen : Car { // Bar
    bool CorrectPolution { get; set; }
}

現在,如果您構建一輛Volkswagen ,十個顯然是CarIDriveable 但是通過說: Car car = (Car) volkswagen; . 沒有改變大眾汽車,你只是現在讓編譯器知道car是一個Car對象。

因為GetType()返回對象的類型。 當您顯式地將派生類轉換為基類時,您沒有創建新實例,您只有現有對象上的新類型引用。 你可以用下面的代碼看到它:

IFoo iFoo = GetIFoo();
Foo foo = (Foo)iFoo;
Console.WriteLine(ifoo.Equals(foo));

您在對象的運行時類型與其編譯時類型之間感到困惑。

IFoo iFoo = GetIFoo();

在上面的行中, iFoo的運行時類型仍然是Bar 這是對象的真實類型。 但是,如果您通過IFoo類型的引用訪問Bar對象,則編譯器只知道該類型是IFoo ,因此稱為引用iFoo指向的對象的編譯時類型。 請注意,在執行GetIFooreturn bar語句時,編譯器丟失了此類型信息。

Foo foo = (Foo)iFoo;

再往下,您可以將其強制轉換為Bar層次結構中您想要的任何類型,任何高於和包括Bar類型都將成功,並且編譯時類型將基於您將其強制轉換為的類型。 但同樣,運行時類型仍將保持不變,即Bar

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM