Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 向上投射及其对堆的影响_C#_Oop_Inheritance_Upcasting - Fatal编程技术网

C# 向上投射及其对堆的影响

C# 向上投射及其对堆的影响,c#,oop,inheritance,upcasting,C#,Oop,Inheritance,Upcasting,对于以下类别: public class Parent { //Parent members } public class ChildA : Parent { //ChildA members } public class ChildB : Parent { //ChildB members } 如果我将ChildA或ChildB实例向上转换为父实例,那么我无法访问它们的成员,但它们的成员仍然存在,因为如果我向下转换并再次尝试访问它们的成员,我会发现它们仍然有数据 我认为这意味着父实例将继

对于以下类别:

public class Parent {
//Parent members
}

public class ChildA : Parent {
//ChildA members
}

public class ChildB : Parent {
//ChildB members
}
如果我将ChildA或ChildB实例向上转换为父实例,那么我无法访问它们的成员,但它们的成员仍然存在,因为如果我向下转换并再次尝试访问它们的成员,我会发现它们仍然有数据

我认为这意味着父实例将继续为子类分配内存

那么,这是否意味着当我实例化一个父类时,它将为子类成员分配内存,或者这只是在我强制转换时发生的


如果我们向前或向后进行强制转换,父类是否可以为多个子类分配内存?

在您上面描述的情况下,强制转换不会影响从基类强制转换到子类时分配的内存,反之亦然

如果实例化一个父对象,内存中将有一个父对象。如果您将其强制转换到任一子类,它将失败,并出现
InvalidCastException

如果实例化其中一个子对象,内存中将有一个子对象。您可以将其强制转换为父级,然后再次转换。在这两种情况下,内存分配都不会改变

此外,如果您实例化ChildA,强制转换到父级,然后尝试强制转换到ChildB,您将获得引用类型的“正常”向上转换和向下转换

对于引用类型,强制转换变量不会更改堆上已分配的对象的类型,它只会影响引用该对象的变量的类型

因此,不存在强制转换引用类型(即类中的对象实例)的额外堆开销

考虑以下类层次结构:

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
向下投射成功,则向下投射类型的所有方法、属性和事件都将可用于变量。如果向下转换失败,将抛出
InvalidCastException
,因为类型系统将在运行时检查堆对象的类型与变量类型的兼容性

转换运算符

根据tolanj的评论,如果替换引用类型的默认强制转换,那么关于堆的所有赌注都将被取消

例如,如果我们添加一个不相关的类:

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的显式操作符Apple可以做任何它喜欢的事情,包括在堆上分配新对象

var wax = new WaxApple();
var fakeApple = (Apple)wax;
// Explicit cast operator called, new heap allocation as per the conversion code. 
我认为这意味着父实例将继续为子类分配内存

不,因为父类不知道它的子类

var a = new ClassA();
.NET
ClassA
的所有成员分配内存

var b = (Parent)a;
.NET
对内存没有任何作用
a
b
指向同一个内存块(分配给
ClassA
)。

a(向下-)转换只不过是“父类的眼睛”对类实例的视图。因此,通过强制转换,您既不会丢失也不会添加任何信息或内存,只需引用为原始实例分配的相同内存allready即可。这就是为什么您仍然可以访问(例如通过反射)类型为
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
类型的实例,它们都消耗自己的内存。

区分对象和变量。创建实例时,就是在创建对象。当您强制转换时,您是在强制转换变量,而不是对象。感谢您的回复,我是这样想的,因为父变量持有一个子变量,并且仍然可以强制转换它并访问其成员,但我知道我得到了它,感谢explain@Honey-用于参考类型(
)变量仅保存指向已分配内存的指针。来回转换不会改变指针,也不会改变分配的内存。它仅仅改变了指针值的解释方式。对于值类型(
struct
,以及
int
float
byte
等基本类型),值存储在变量本身中。由于无法继承值类型,大多数强制转换(如从
int
float
)只需执行一些转换并将结果存储在目标变量中。强制转换不会影响在任何情况下分配的内存。->对于这种类型的强制转换(子类->父类,类->实现接口等),为true,可能值得注意的是,这仅在“存在隐式/显式cast运算符、使用并分配内存的情况除外”的任何情况下才是正确的@亲爱的-大多数情况下没有额外的内存分配,除非您在cast运算符中做了一些奇特的操作。实际上,
class
es也是如此-如果定义了显式/隐式cast操作符,则所有赌注都是无效的。最后,可以将值类型强制转换为
对象
(这是一种引用类型),这涉及到“装箱”-thi