如何在 C# 中将父对象强制转换为子对象

How it is possible to cast parent to child in c#

前几天在面试中,被要求解释以下案例:

public class Parent
{

}
public class Child:Parent
{

}

案例一:

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

案例2:

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

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

Why case2 is invalid while both p variable in case1 and case2 are Type of Parent class?

在案例 2 中,我们通过以下消息得到 运行时间异常:

Unable to cast object of type 'Parent' to type 'Child

而在 case1 p 中变量也是 Parent class.

的类型

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

你有什么想法吗?

更新:

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

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

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

赋值后p的值的运行时间类型第一种情况是Child,第二种情况是Parent。如果 pnull.

则不确定

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

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

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

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

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


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

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

这可以避免在 p 的 运行 时间类型为 Parent 的情况下出现异常。