这是初学者的问题,但我有兴趣了解这里发生了什么。我的问题是,当你向下投射一个物体时幕后会发生什么?它是否保留了关于它最初的内容的某种元数据?这就是我的意思:
假设我有一个名为“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”是一名经理,即使我已将他作为员工传递给他?代码如何知道他确实可以被投射到经理?堆中有什么东西在发生吗?
答案 0 :(得分:13)
要记住的第一件事是, 根本不会更改原始对象。它只会通过该特定引用更改对象的视图。多个引用可以指向同一个对象,因此更改对象对于转换是不合理的。
您在实例中可能会做的是ClockIn()
Employee
类的方法。然后,当你打电话
bob.ClockIn();
然后bob
将知道真正的类型,并为其类型调用适当的ClockIn()
方法。这称为dynamic method dispatch,不适用于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() {
....
}
}
答案 1 :(得分:5)
答案 2 :(得分:3)
Casting不会影响对象 - 它会影响对象的引用。
当你向下转换一个对象时,你所做的就是告诉编译器这个对象可以被一个从当前引用该对象的类型派生的变量引用。
关键点是对象的类永远不会改变,你只是将引用的类型更改为对象。
答案 3 :(得分:1)
实际的对象类型始终存储为其System.Type
的链接。实际上,每个对象.NET都有一个引用其实际类型的附加System.Type
字段。
答案 4 :(得分:0)
扩展上述答案,将类的公共方面视为电气面板是有帮助的。固定的外部世界连接点附加到类的内部工作。如果一个类继承了另一个类,那么除了具有基类类型的面板之外,新类的对象还会获得一个标有新类类型的面板。添加界面会添加另一个面板。
如果声明属性或方法可覆盖,则该方法/属性的面板“connection”的背面将具有可拆卸的插头;否则它不会。假设类“Alpha”具有可覆盖的方法“foo”和不可覆盖的函数“bar”。派生类“Bravo”会覆盖“foo”和阴影“bar”。 “Bravo”类的对象将同时具有“Alpha.foo”和“Bravo.foo”连接到Bravo的“foo”功能;他们将“Alpha.Bar”连接到Alpha的“Bar”功能,“Bravo.Bar”连接到Bravo的“Bar”功能。
如果某个“Bravo”类型的对象“BravoInstance”用于需要“Bravo”的位置,则对其“BravoInstance.Bar”的引用将使系统查看对象的“Bravo”面板并使用其“Bar”连接(连接到Bravo.Bar)。如果这样的实例被赋予了期望Alpha的代码(由于继承而完全允许),则尝试访问ThatInstance.Bar将连接到“Alpha”面板的“Bar”连接(后者又连接到Alpha.Bar)
有时阴影是有用且合适的,但是在遮蔽对象的方法或属性时必须非常小心,这可能会传递给需要基类型的代码。