1

我正在学习拳击和拆箱。

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

谁能给我解释一下。

通过看一个简单的例子,我知道装箱和拆箱现在做了什么,但是这个例子有点混乱。

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

[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呢?

4

5 回答 5

4

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

  • 当您将 astruct转换为object时,它会被复制到一个新的object中。

  • 当您将 anobject转换为 astruct时,它会被复制到一个新的struct中。

  • classes 之间进行转换时,不会复制对象的内容;仅复制参考。

希望有帮助。

于 2013-08-05T03:04:37.773 回答
1

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

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

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

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

于 2013-08-05T02:57:45.797 回答
0

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

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

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

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

至于你的困惑

object o = p; is boxing

然而

(Point)o is unboxing
于 2013-08-05T01:47:56.163 回答
0

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

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

“值类型是内联分配的。引用类型总是在堆上分配的”

意思是,如果你在引用类型中添加一个值类型(struct、int、float、bool)作为类变量(公共或私有),则该值类型的数据将嵌入到该引用类型位于堆上的任何位置.

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

因此,鉴于背景知识,当您“装箱”一个值类型时会发生什么应该是不言自明的:您必须采用该值类型(无论它是在线分配的哪里)并将其转换为引用类型(在堆上为它创建一个新对象)。

于 2013-08-05T02:01:56.807 回答
0

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

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

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

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

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

于 2013-08-05T02:15:33.393 回答