何时结构体不存储在堆栈中?

5
我正在阅读Jon Skeet的书评,他正在讨论《Head First C#》众多不准确之处
其中一个引起了我的注意:

[在错误部分]声称结构体总是存储在堆栈上。

在什么情况下,结构体会不存储在堆栈上?这与我对结构体的认识相反。
6个回答

7

但是结构体几乎总是作为堆上分配的对象的成员,这不是在结构体只是局部变量的情况下吗?这不是在任何地方都是这种情况吗? - Simon_Weaver
几乎无处不在。结构体也可以是类的静态成员,在这种情况下,它不是对象的成员,但仍然分配在堆上。 - yfeldblum
@Simon_Weaver 传递给方法的是值类型变量。它不必在方法中定义。你把机制和使用频率混淆了。机制才是重要的,而不是它被使用的频率。 - Lee Louviere

4
无论何时它们是类上的一个字段
这种情况的不寻常例子:
a:当值类型变量被捕获时:
int i = 2;
Action action = delegate {i++;}
action();
Console.WriteLine(i);

这将被编译成类似以下的内容:
class Foo {
    public int i;
    public void Bar() {i++;}
}
...
Foo foo = new Foo();
foo.i = 2;
Action action = foo.Bar;
action();
Console.WriteLine(foo.i);

b: 当值类型变量在迭代器块中使用时:

IEnumerable<int> GetValues() {
   for(int i = 0 ; i < 5 ; i++) yield return i;
}

编译器生成一个状态机来表示迭代器,其中所有局部变量(例如i)都是字段。

2
值类型的实例被装箱时,盒子和实例本身会被移动到堆上。虽然,毫无疑问,非类成员值类型实例在首次创建时总是在堆栈上创建。
结构体是值类型。因此,它的行为与上述相同。

盒装类型本身实际上是引用类型,注意了。任何值类型都有一种类似阴影引用类型版本来进行装箱。这有点复杂。在我看来,“属于类的成员”情况要少得多 :) - Jon Skeet
从技术上讲,值类型被复制到盒子的成员中,该盒子已经存在于堆上。因此它并没有“移动”到堆上。术语“装箱类型”是一个误称。如果您希望将值类型作为引用类型使用,最好将其作为引用类型。值类型应保留用于不可变的或通过管道传输数据到第三方。 - Lee Louviere

2

这里举一个例子,来自于1800 INFORMATION的回答

public class Foo
{
    int x;

    public Foo(int y)
    {
        x = y;
    }
}

...

Foo foo = new Foo(10);

现在,在构造函数执行完成后,foo.x的值为10。 foo.x在内存中的位置在堆上。 foo.x的类型是int,也称为System.Int32,它是一个结构体。

关于捕获变量和装箱等的其他答案也是正确的(在装箱情况下有点不同-我会添加注释),但是在我看来,这个例子是最简单和最重要的。


有结构体和枚举 - 我相信就这些。 - Jon Skeet
.Net很奇怪。int是一个结构体。作为C++开发人员,这需要一些时间来适应。 - 1800 INFORMATION
很多简单类型都是值类型。我认为这样做是为了反映来自大多数其他语言的人的期望和出于效率的考虑。复制一个Int16会更好,不是吗?而将Int32作为引用会使它的大小加倍。 - Lee Louviere

0

已经被其他人深入讨论过了

  • 成员字段值
  • 装箱结构体(严格来说,它们不再是值类型)

此外:

  • 结构体数组完全驻留在堆上
  • 静态结构体(不在堆栈或堆上,而是在它们自己的特殊区域中)

结构体数组 / 数组 / 静态 == 成员字段 - Lee Louviere

0
该书错误地将打字机制与范围机制混淆了。 值类型不是引用类型。当您在调用一个将其作为参数(无需 ref 或 out)的方法时,它的数据会被复制。 值类型将存在于方法的作用域栈上,而不是像引用对象一样存在于堆上,但这并不意味着它总是存在于堆栈上。
所以,该书在使用陈述中代替机械陈述,并混淆了两者。这不是完全没有根据的说法,但是是错误的。
值类型将被保存在其所有者所在的位置。
因此,如果其范围是一个方法,则它们将存在于堆栈上。如果它们的范围是一个对象,则它们将随该对象一起存在于堆上。
有了这个,可以很放心地说,最好将值类型保留为不可变类型,因为大多数人很难预测按值复制的值类型机制。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接