当一个对象被强制转换为基类时,它如何记住它到底是什么?

这是初学者的问题,但我有兴趣了解这里发生了什么。 我的问题是,当您向下投射一个物体时幕后会发生什么? 它是否保留了关于它最初的内容的某种元数据? 这就是我的意思:

假设我有一个名为“ClockIn”的方法,它接受“Employee”类型的参数:

public static void ClockIn(Employee employee) { var manager = employee as Manager; if (manager != null) { manager.OpenSafe(); } } 

因此,假设Manager是Employee类型的子类,并且它具有“OpenSafe”方法:

 public class Manager : Employee { public void OpenSafe() { ... } } 

“ClockIn”方法,如果发现已传入Manager,则调用OpenSafe方法。 如:

 var bob = new Manager(); ClockIn(bob); 

在这里,我将一个类型为Manager的实例传递给一个接受基类Employee的方法。 在调用OpenSafe之前,我需要将ClockIn方法中的实例强制转换为Manager。

问题是,是否有一些元数据记得“bob”是一名经理,即使我已经将他作为员工传递给他? 代码如何知道他确实可以被投射给经理? 堆里有什么东西在发生吗?

首先要记住的是,转换根本不会改变原始对象。 它只会通过该特定参考更改您对象的视图 。 多个引用可以指向同一个对象,因此更改对象对于转换是不合理的。

您可以在实例中执行的操作是使ClockIn()成为Employee类的方法 。 然后,当你打电话

 bob.ClockIn(); 

那么bob会知道他到底是什么类型,并为他的类型调用适当的ClockIn()方法。 这称为动态方法调度 ,不适用于示例中的static函数。

例如:

 public class Employee { public void ClockIn() { .... } } public class Manager: Employee { public void ClockIn() { // first, do what all Employees do when clocking in Employee.ClockIn(); // Next, do Manager specific actions OpenSafe(); } public void OpenSafe() { .... } } 

强制转换不会更改对象的运行时类型。

在对象上调用GetType方法将返回表示其运行时类型的Type对象。

转换不会影响对象 – 它会影响对象的引用。

向下转换对象时所做的就是告诉编译器该对象可以由从当前引用该对象的类型派生的变量引用。

关键点是对象的类永远不会改变,你只是改变了对象的引用类型

实际的对象类型始终存储为其System.Type链接。 实际上,每个对象.NET都有一个引用其实际类型的附加System.Type字段。

扩展上述答案,将类的公共方面视为电气面板是有帮助的。 固定的外部世界连接点附加到类的内部工作。 如果一个类inheritance了另一个类,那么除了具有基类类型的面板之外,新类的对象还会获得一个标有新类类型的面板。 添加界面会添加另一个面板。

如果声明属性或方法可覆盖,则该方法/属性的面板“连接”的后面将具有可拆卸的插头; 否则它不会。 假设类“Alpha”具有可覆盖的方法“foo”和不可覆盖的函数“bar”。 派生类“Bravo”会覆盖“foo”和阴影“bar”。 “Bravo”类的对象将同时具有“Alpha.foo”和“Bravo.foo”连接到Bravo的“foo”function; 他们将“Alpha.Bar”连接到Alpha的“Bar”function,“Bravo.Bar”连接到Bravo的“Bar”function。

如果在预期“Bravo”的地方使用“Bravo”类型的对象“BravoInstance”,则对其“BravoInstance.Bar”的引用将使系统查看对象的“Bravo”面板并使用其“Bar”连接(连接到Bravo.Bar)。 如果这样的实例被赋予了期望Alpha的代码(由于inheritance而完全允许),尝试访问ThatInstance.Bar将连接到“Alpha”面板的“Bar”连接(后者又连接到Alpha.Bar) 。

有时阴影是有用且合适的,但是在遮蔽对象的方法或属性时必须非常小心,这可能会传递给期望基类型的代码。