【问题标题】:How it is possible to cast parent to child in c#如何在 C# 中将父级转换为子级
【发布时间】:2021-08-29 18:21:25
【问题描述】:

前几天在一次采访中,我被要求解释以下案例:

public class Parent
{

}
public class Child:Parent
{

}

案例1:

Parent p = new Child();
    var c = (Child)p;

案例2:

    Parent p = new Parent();
    var c = (Child)p;

我说case1有效,case2无效,但他问:

为什么 case2 无效,而 p 变量在 case1case2Parent 类的类型吗?

在 case2 中,我们通过以下消息得到运行时异常:

无法将“父”类型的对象转换为“子”类型

虽然在case1 p 变量也是Parent 类的类型。

他要我从stackheap 记忆的角度来解释。

你有什么想法吗?

更新:

面试官误导我的地方正是他在两种情况下都说p 是“父”类的类型,根据答案,两个变量的静态类型都是“父”,但在运行时它们有不同的类型,真正重要的是运行时类型。

【问题讨论】:

标签: c# oop stack heap-memory


【解决方案1】:

您必须区分变量的静态类型(编译时已知的类型)和分配给变量的对象引用的运行时类型。

p 的静态类型在这两种情况下都是Parent,因为它被声明为Parent p。不管你分配什么。你甚至可以分配它null

赋值后p的值的运行时类型在第一种情况下是Child,在第二种情况下是Parentp 是否为 null 将无法确定。

可以将Child 分配给Parent,因为Child 类派生自Parent。因此,ChildParent(在 OO 术语中)。

但是,Parent 不是Child,因为它不是从它派生的。因此,在第二种情况下,您不能将p 转换为Child。在第一种情况下,转换是有效的,因为您将对象转换为其实际运行时类型。它告诉编译器,我知道分配给Parent 变量的这个对象是Child,所以,请把它当作Child。这不涉及任何转换;但是,如果不允许强制转换,则会执行运行时检查,可能会引发异常。

你被要求从堆栈和堆内存的角度来解释它。很抱歉,但这与堆栈或堆内存完全无关。它与继承规则和赋值兼容性有关。

当然,这里我们处理的是引用类型(类)。这允许我们从Parent 派生Child。这对于值类型(结构)是不可能的。重点是引用类型与值类型,而不是堆与堆栈,这是一个实现细节。顺便说一句,类中的值类型(结构)字段也将存储在堆上。

另请参阅:The Stack Is An Implementation Detail, Part OneTwo(Eric Lippert 的博客)


您没有要求它,但转换为派生类型通常会先进行类型测试,因为您通常事先不知道运行时类型。假设Child 类添加了一个新方法DriveParentsToDespair()(只有孩子可以这样做)。然后您可以使用Type testing with pattern matching 测试类型并同时将其分配给新变量:

if (p is Child child) {
    child.DriveParentsToDespair();
}

这避免了p 的运行时类型为Parent 的异常。

【讨论】:

  • 这是一种“动态多态性”吗?
  • @Abolfazl It IS 多态性(动态),如我的 cmets 所示:操纵一个只知道祖先的孩子 (使用向上转换)。
  • 静态和编译时类型是个好点。
  • 多态是动态的,当你调用一个虚方法时就会发挥作用。例如。如果Parent 有一个虚拟打印方法打印“我是父母”到控制台并且Child 类确实覆盖了这个方法让它打印“我是孩子”,那么调用p.Print(); 的结果取决于p 的运行时类型。
  • 您在回答中提到的确切点是我们需要区分静态类型和运行时类型。
猜你喜欢
  • 2018-05-16
  • 2015-10-20
  • 1970-01-01
  • 1970-01-01
  • 2014-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多