引用类型的“正常”向上转型和向下转型
对于引用类型,强制转换变量不会改变已在堆上分配的对象的类型,它只影响引用该对象的变量的类型。
因此,在使用引用类型(即来自类的对象实例)进行转换时,通常不会有额外的堆开销,
前提是没有涉及自定义转换运算符(请参阅下面tolanj的评论)。
考虑以下类层次结构:
public class Fruit
{
public Color Colour {get; set;}
public bool Edible {get; set;}
}
public class Apple : Fruit
{
public Apple { Color = Green; Edible = true; KeepsDoctorAtBay = true;}
public bool KeepsDoctorAtBay{get; set;}
}
当同时使用向上转型和向下转型时:
![Example of variables pointing to same heap object](https://istack.dev59.com/2TUS6.webp)
堆上只有一个分配,即最初的var foo = new Apple()
。
在各种变量赋值后,所有三个变量foo
、bar
和baz
都指向同一个对象(堆上的Apple
实例)。
向上转型(Fruit bar = foo
)将仅限制变量可用访问的Fruit
方法和属性,如果(Apple)bar
向下转型成功,则所有向下转型类型的方法、属性和事件都将对变量可用。如果向下转型失败,则会抛出InvalidCastException
,因为类型系统将在运行时检查堆对象的类型与变量类型的兼容性。
转换运算符
根据tolanj的评论,如果显式转换运算符替换引用类型的默认转换,则关于堆的所有赌注都无效。
例如,如果我们添加一个不相关的类:
public class WaxApple
{
public static explicit operator Apple(WaxApple wax)
{
return new Apple
{
Edible = false,
Colour = Color.Green,
KeepsDoctorAtBay = false
};
}
}
正如你所想象的那样,WaxApple的
explicit operator Apple
可以随心所欲地进行操作,包括在堆上分配新对象。
var wax = new WaxApple();
var fakeApple = (Apple)wax;