向上转型及其对堆的影响

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()

在各种变量赋值之后,foobarbaz这三个变量都指向同一个对象(堆上的一个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();

.NETClassA.

的所有成员分配内存
var b = (Parent)a;

.NET 不对内存做任何事情。 ab 指向同一个内存块(分配给 ClassA)。

(向下)转换只不过是 视图 到 "eyes of the parent class" 的 class 实例。因此,您既不会丢失也不会添加任何信息或内存,您只需 引用已为原始实例分配的相同内存 。这就是为什么您仍然可以访问(例如通过反射)Parent 类型变量中 ChildA 的成员的原因。信息依然存在,只是不可见。

所以你有两个内存-引用.

而不是两个内存-分配

但是请注意,如果您提供自己的演员阵容,这 不适用 ,例如从 ChildAChildB。这样做通常看起来或多或少类似于:

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 类型,它们都消耗自己的内存。