[英]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?
為什么如果我將
iFoo
為Foo
,foo
的類型是Bar
而不是Foo
?
這是因為強制轉換改變了靜態類型。 它對動態類型沒有影響。 foo
的靜態類型是Foo
,但它的動態類型是Bar
。
每個變量都有一個編譯器已知的類型(靜態類型)和一個運行時已知的類型(動態類型)。 在某些情況下,這兩種類型是相同的,但它們不必一直相同。
靜態類型是變量的聲明類型。 動態類型是在將對象分配給該變量后通過調用GetType
獲得的。
當變量的靜態類型與其動態類型不匹配時,您構造了一個示例: iFoo
靜態類型為IFoo
,但其動態類型為Bar
。 這完全沒問題,因為Bar
與IFoo
兼容,因為它實現了接口,也與Foo
兼容,因為它擴展了類。
這真的是繼承 101 :創建的對象是一個Bar
並且強制轉換只會影響您查看它的方式,而不是它的真正含義。
在這里,您將其視為IFoo
:
IFoo iFoo = GetIFoo();
在這里,您故意將其視為Foo
。 如果它不是Foo
或其任何后代,您會在運行時得到InvalidCastException
。
Foo foo = (Foo)iFoo;
Bar
實例可以合法地作為Bar
、 Foo
和IFoo
(為了完整起見,也可以作為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
,十個顯然是Car
和IDriveable
。 但是通過說: 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
指向的對象的編譯時類型。 請注意,在執行GetIFoo
的return bar
語句時,編譯器丟失了此類型信息。
Foo foo = (Foo)iFoo;
再往下,您可以將其強制轉換為Bar
層次結構中您想要的任何類型,任何高於和包括Bar
類型都將成功,並且編譯時類型將基於您將其強制轉換為的類型。 但同樣,運行時類型仍將保持不變,即Bar
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.