无法理解拳击是如何完成的

时间:2013-08-05 01:46:27

标签: c#

我正在研究拳击和拆箱。

我经历了这个例子,我无法理解答案。

请有人向我解释。

通过查看一个简单的例子,我知道拳击和拆箱现在做了什么,但是这个例子让人有点困惑。

拳击然后拆箱的一个例子,一个棘手的例子。

[struct|class] Point {
    public int x, y;    
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}    
Point p = new Point(1, 1);
object o = p; p.x = 2;
Console.WriteLine(((Point)o).x);

我把答案读作:

这取决于!如果Point是一个结构,那么输出是1,但如果Point是一个类,那么输出是2!装箱转换会复制正在装箱的值,以解释行为的差异。

这是((point)o).x拳击还是拆箱?

不明白,请有人向我解释。

我知道答案应该是1,但如果上课那么2?

5 个答案:

答案 0 :(得分:4)

我不知道为什么每个人都在写一篇文章,解释起来很简单:

  • 当您将struct投射到object时,复制到新的object

  • 当您将object投射到struct时,它会复制到新的struct

  • class es之间投射时,不会复制对象的内容 ;只复制参考

希望有所帮助。

答案 1 :(得分:1)

虽然C#试图假装结构类型来自Object,但这只是半真的。根据CLI规范,结构类型规范实际上定义了两种东西:一种派生自System.ValueType(依次为System.Object)的堆对象,以及一种存储位置(局部变量) ,静态变量,类字段,结构域,参数或数组槽),它们不是从任何派生的,而是可以隐式转换为堆对象类型。

每个堆对象实例都包含由类型或其父类(如果有)定义的所有字段,以及标识其类型的标头和有关该实例的一些其他信息。每个struct-type存储位置都包含保存其值所需的字节(如果是基本类型),或者包含其所有字段的连接值;在这两种情况下,它都不包含任何标识其类型的标头。相反,值类型依赖于生成的代码中的信息来了解它们是什么。

如果将值类型存储到该值类型的存储位置,编译器将使用从原始值类型获取的值覆盖目标占用的所有字节。但是,如果尝试将值类型存储到引用类型存储位置(如Object),则运行时将生成一个具有足够空间的新堆对象,以容纳值类型中的所有数据,以及标识其类型的标头,并在目标位置存储对该新对象的引用。如果尝试将引用类型类型转换为值类型,则运行时将首先验证该对象的类型是否正确,如果是,则将数据从堆对象复制到目标。

有一些涉及接口和泛型的棘手场景。接口类型是引用类型,但是如果结构实现了接口,则实现方法可以直接作用于盒装结构实例,而无需对其进行拆箱和重新加箱。此外,用作通用约束的接口类型不需要装箱。如果将值类型的变量(如List<int>.Enumerator)传递给函数EnumerateThings<TEnumerator>(ref TEnumerator it) where TEnumerator: IEnumerator<int>,则该方法将能够接受对该变量的引用而不进行装箱。

答案 2 :(得分:0)

由于Point是Struct,而Structs是值类型,这意味着它们在传递时会被复制。

因此,如果您更改副本,则只更改该副本,而不是原始副本。

但是如果Point是一个类,那么它是通过引用传递的。

因此,如果您更改副本,则只会更改该副本以及原始副本。

至于你的困惑

object o = p; is boxing

,而

(Point)o is unboxing

答案 3 :(得分:0)

要了解拳击,您需要了解值类型和参考类型之间的区别。

我认为理解它的最简单方法是:

  

“值类型是内联分配的。引用类型始终是   分配在堆“

意思是,如果在引用类型中添加值类型(struct,int,float,bool)作为类变量(公共或私有),那么值类型的数据将被嵌入到引用类型生活的任何地方在堆上。

如果在函数内部创建值类型,但不将其分配给公共/私有变量,则在函数堆栈中分配该值类型(意味着一旦离开该函数,它将被收集)

所以,考虑到背景知识,当你“选择”一个值类型时会发生什么,这应该是非常明显的:你必须采用那个值类型(无论它在哪里都是在线的)已分配)并将其转换为引用类型(在堆上为它创建一个新对象)。

答案 4 :(得分:0)

首先,您需要知道对象的存储位置。结构,枚举和其他值类型存储在堆栈,寄存器或堆中;类和其他引用类型存储在堆上。一个好的教程是here

当值类型存储在堆中时完成装箱。正在从堆栈到堆栈进行复制。反向装箱是另一种方式,值的副本将从堆到堆栈。

   1 Point p = new Point(1, 1);
   2 object o = p; 
   3 p.x = 2;
   4 Console.WriteLine(((Point)o).x);

在上面的代码中,如果Point是结构,则会对对象“o”进行复制。在第3行,您修改的是堆栈中的Point。在最后一行,您将对象“o”取消装箱,但您将获得的值是从堆中复制的值。

如果Point是class,则在第1行中,为堆中的Point创建一个空格。第2行,创建一个引用相同内存空间的新变量“o”。请记住“p”&amp; “o”引用相同的内存地址位置,因此如果您修改任何变量,就像第3行一样,您将获得两个变量的修改值。