向上转型及其对堆的影响
Upcasting and its effect on the heap
以下类:
public class Parent {
//Parent members
}
public class ChildA : Parent {
//ChildA members
}
public class ChildB : Parent {
//ChildB members
}
如果我将 ChildA 或 ChildB 实例向上转换为 Parent 实例,那么我无法访问它们的成员,但它们的成员仍然存在,因为如果我垂头丧气,再次尝试访问他们的成员,我会发现他们仍然有他们的数据。
我认为这意味着 Parent 实例继续为 Child 类 分配内存。
这是否意味着当我实例化一个 Parent Class 时它为 child 类 成员分配内存,或者这只是在我投射时发生?
如果我们通过转换来回移动,parent 是否可以为多个 child 分配内存?
在您上面描述的情况下,转换不会影响从基转换到子 class 时分配的内存,反之亦然。
如果你实例化一个 Parent 你将在内存中有一个 Parent object。如果您将其转换为 child classes 中的任何一个,它将失败并显示 InvalidCastException
.
如果您实例化任一 child,您将在内存中有一个 child object。您可以将其转换为 Parent 然后再返回。在任何一种情况下,内存分配都不会改变。
此外,如果您实例化一个 ChildA,转换为 Parent,然后尝试转换为 ChildB,您将得到一个 InvalidCastException
引用类型的“正常”向上转换和向下转换
对于引用类型,强制转换变量不会改变已经在堆上分配的对象的类型,它只会影响引用该对象的变量的类型。
所以不,转换引用类型(即来自 classes 的对象实例)没有任何额外的堆开销前提是不涉及自定义转换运算符(见下文,tolanj 的评论)。
考虑以下 class 层次结构:
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;}
}
其中,当与向上转型和向下转型一起使用时:
堆上只有一次分配,即初始 var foo = new Apple()
。
在各种变量赋值之后,foo
、bar
和baz
这三个变量都指向同一个对象(堆上的一个Apple
实例)。
向上转换 (Fruit bar = foo
) 将简单地限制变量的可用访问权限仅 Fruit
方法和属性,如果 (Apple)bar
向下转换成功,则变量的所有方法、属性和事件downcast 类型将可用于变量。如果向下转换失败,将抛出 InvalidCastException
,因为类型系统将在 运行 时检查堆对象的类型与变量类型的兼容性。
转换运算符
根据 tolanj 的评论,如果 explicit conversion operator 替换了引用类型的默认转换,那么所有关于堆的赌注都会被取消。
例如,如果我们添加一个不相关的 class:
public class WaxApple // Not inherited from Fruit or Apple
{
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;
// Explicit cast operator called, new heap allocation as per the conversion code.
I think this means that the Parent Instance keep allocating memory for the Child classes .
不,因为 Parent
class 不知道它是 children。
var a = new ClassA();
.NET
为 ClassA
.
的所有成员分配内存
var b = (Parent)a;
.NET
不对内存做任何事情。 a
和 b
指向同一个内存块(分配给 ClassA
)。
(向下)转换只不过是 视图 到 "eyes of the parent class" 的 class 实例。因此,您既不会丢失也不会添加任何信息或内存,您只需 引用已为原始实例分配的相同内存 。这就是为什么您仍然可以访问(例如通过反射)Parent
类型变量中 ChildA
的成员的原因。信息依然存在,只是不可见。
所以你有两个内存-引用.
而不是两个内存-分配
但是请注意,如果您提供自己的演员阵容,这 不适用 ,例如从 ChildA
到 ChildB
。这样做通常看起来或多或少类似于:
public static explicit operator ChildA(ChildB b)
{
var a = new ChildA((Parent)b);
/* set further properties defined in ChildA but not in ChildB*/
}
这里有两个完全不同的实例,一个是 ChildA
类型,另一个是 ChildB
类型,它们都消耗自己的内存。
以下类:
public class Parent {
//Parent members
}
public class ChildA : Parent {
//ChildA members
}
public class ChildB : Parent {
//ChildB members
}
如果我将 ChildA 或 ChildB 实例向上转换为 Parent 实例,那么我无法访问它们的成员,但它们的成员仍然存在,因为如果我垂头丧气,再次尝试访问他们的成员,我会发现他们仍然有他们的数据。
我认为这意味着 Parent 实例继续为 Child 类 分配内存。
这是否意味着当我实例化一个 Parent Class 时它为 child 类 成员分配内存,或者这只是在我投射时发生?
如果我们通过转换来回移动,parent 是否可以为多个 child 分配内存?
在您上面描述的情况下,转换不会影响从基转换到子 class 时分配的内存,反之亦然。
如果你实例化一个 Parent 你将在内存中有一个 Parent object。如果您将其转换为 child classes 中的任何一个,它将失败并显示 InvalidCastException
.
如果您实例化任一 child,您将在内存中有一个 child object。您可以将其转换为 Parent 然后再返回。在任何一种情况下,内存分配都不会改变。
此外,如果您实例化一个 ChildA,转换为 Parent,然后尝试转换为 ChildB,您将得到一个 InvalidCastException
引用类型的“正常”向上转换和向下转换
对于引用类型,强制转换变量不会改变已经在堆上分配的对象的类型,它只会影响引用该对象的变量的类型。
所以不,转换引用类型(即来自 classes 的对象实例)没有任何额外的堆开销前提是不涉及自定义转换运算符(见下文,tolanj 的评论)。
考虑以下 class 层次结构:
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;}
}
其中,当与向上转型和向下转型一起使用时:
堆上只有一次分配,即初始 var foo = new Apple()
。
在各种变量赋值之后,foo
、bar
和baz
这三个变量都指向同一个对象(堆上的一个Apple
实例)。
向上转换 (Fruit bar = foo
) 将简单地限制变量的可用访问权限仅 Fruit
方法和属性,如果 (Apple)bar
向下转换成功,则变量的所有方法、属性和事件downcast 类型将可用于变量。如果向下转换失败,将抛出 InvalidCastException
,因为类型系统将在 运行 时检查堆对象的类型与变量类型的兼容性。
转换运算符
根据 tolanj 的评论,如果 explicit conversion operator 替换了引用类型的默认转换,那么所有关于堆的赌注都会被取消。
例如,如果我们添加一个不相关的 class:
public class WaxApple // Not inherited from Fruit or Apple
{
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;
// Explicit cast operator called, new heap allocation as per the conversion code.
I think this means that the Parent Instance keep allocating memory for the Child classes .
不,因为 Parent
class 不知道它是 children。
var a = new ClassA();
.NET
为 ClassA
.
var b = (Parent)a;
.NET
不对内存做任何事情。 a
和 b
指向同一个内存块(分配给 ClassA
)。
(向下)转换只不过是 视图 到 "eyes of the parent class" 的 class 实例。因此,您既不会丢失也不会添加任何信息或内存,您只需 引用已为原始实例分配的相同内存 。这就是为什么您仍然可以访问(例如通过反射)Parent
类型变量中 ChildA
的成员的原因。信息依然存在,只是不可见。
所以你有两个内存-引用.
而不是两个内存-分配但是请注意,如果您提供自己的演员阵容,这 不适用 ,例如从 ChildA
到 ChildB
。这样做通常看起来或多或少类似于:
public static explicit operator ChildA(ChildB b)
{
var a = new ChildA((Parent)b);
/* set further properties defined in ChildA but not in ChildB*/
}
这里有两个完全不同的实例,一个是 ChildA
类型,另一个是 ChildB
类型,它们都消耗自己的内存。