[英]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.