在C#中将派生类转换为基类

时间:2017-06-11 10:44:49

标签: c# inheritance polymorphism

我正在努力通过投射来理解这种情况。 出于演示目的,我创建了这些类:

    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?< / p>

6 个答案:

答案 0 :(得分:2)

  

为什么如果我将iFoo转换为Foofoo的类型为Bar而不是Foo

这是因为强制转换会改变静态类型。它对动态类型没有影响。 foo静态类型为Foo,但其动态类型为Bar

每个变量都有一个编译器已知的类型(静态类型)和运行时已知的类型(动态类型)。在某些情况下,这两种类型是相同的,但它们不必一直相同。

静态类型是变量的声明类型。动态类型是在将对象分配给该变量后通过调用GetType获得的。

当一个变量的静态类型与其动态类型不匹配时,您构建了一个示例:iFoo的静态类型为IFoo,但其动态类型为Bar。这非常好,因为BarIFoo兼容,因为它实现了接口,也与Foo兼容,因为它扩展了类。

答案 1 :(得分:1)

这真的是inheritance 101:创建的对象是Bar,而广告只会影响您查看的方式,而不会影响真正的

您可以在此处将其视为IFoo

IFoo iFoo = GetIFoo();

在这里,你刻意将其视为Foo。它不是Foo或其任何后代,您在运行时获得InvalidCastException

Foo foo = (Foo)iFoo;

Bar个实例可以合法地用作BarFooIFoo(以及Object为了完整性)。无论您如何查看实例,它仍然是Bar

答案 2 :(得分:1)

Type.GetType为您提供基础实例的类型(即new之后指定的类型),无论哪种类型的变量持有它。此外,foo is Foo不仅true给出了Foo实际实例为Foo,还提供了从class Emitter<O, OK extends keyof O, OV extends O[OK] & {(data:D):void}, D> { constructor(public options: O, public name: OK) { } emit(data: D) { this.options[this.name] && this.options[this.name](data); } } let options: { onSave: (data: string) => void, size: number }; let myEmitter1 = new Emitter(options, "onSave"); // I need error next line about "size" type. let myEmitter2 = new Emitter(options, "size"); 派生的每种类型。

答案 3 :(得分:1)

因为 GetType()返回对象类型。当您明确地将派生类转换为基类时,您没有创建新实例,您只对现有对象有新的类型引用。您可以使用以下代码查看它:

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

答案 4 :(得分:1)

您在对象的运行时类型与其编译时类型之间感到困惑。

- public
  - css
    - bootstrap.css
- src
  - myapp.cr
  - views
    - layout
      - standard.ecr   

在上面一行中,IFoo iFoo = GetIFoo(); 的运行时类型仍为iFoo。这是对象的真实类型。但是如果通过类型为Bar的引用访问Bar对象,则编译器只知道类型为IFoo,因此称为引用所指向的对象的编译时类型{ {1}}。请注意,当执行IFoo中的iFoo语句时,编译器丢失了此类型信息。

return bar

再往下,您可以将其转换为GetIFoo层次结构中所需的任何类型,上面包括Foo foo = (Foo)iFoo; 的任何内容都将成功,编译时类型将基于您键入的内容 - 把它铸成了。但是,运行时类型仍将保持不变,即Bar

答案 5 :(得分:0)

因为行:

Foo foo = (Foo) iFoo;

转换:Bar同时是FooIFoo。因此,当您转向(Foo)时,C#解释器会注意到iFooBar,因此保持对象不变。关键是,从现在开始,编译器将假定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;。你改变大众汽车,你现在只让编译器知道carCar对象。