繁体   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