我的印象是在C#中,struct元素在堆栈上分配,因此从创建它们的方法返回时会消失。但是如果我将struct-values放在一个列表中并返回它会发生什么?元素幸存下来。 有时在堆上分配struct实例吗?
internal struct Stru
{
public int i;
}
internal class StruTry
{
public List<Stru> Get(int max)
{
var l = new List<Stru>();
for (int i = 0; i < max; i++)
l.Add(new Stru {i=i});
return l;
}
}
代码打印0,1,2
[Test]
public void T()
{
var ll = new StruTry().Get(3);
foreach (var stru in ll)
Console.WriteLine("* "+ stru.i);
}
答案 0 :(得分:53)
首先,请阅读Eric Lippert在The Stack is an Implementation Detail上的这篇文章。使用The Truth about Value Types关注它。 至于你的具体问题
是否有时在堆上分配了struct实例?
是的,它们有时会在堆上分配。有很多例子可以在堆上分配它们。如果它们是盒装的,或者它们是类中的字段,或者它们是数组的元素,或者它们是已被关闭的值类型变量的值等等。
但是如果我将struct-values放在一个列表中并返回它会发生什么?元素幸存下来。
您正在考虑这种方式,这是可能分配值类型的重点之一。有关更多详细信息,请参阅我在“关于值类型的真相”中提到的第二篇文章。但请记住,堆栈是一个实现细节。关键的一点是,你真的不需要关心这些东西。您应该关注值类型和引用类型之间的语义差异。
答案 1 :(得分:15)
结构就像整数。如果你有一个本地int,它通常会在堆栈上,如果你有一个int列表,它们会直接存储在列表的内部数组中。结构的行为方式相同。
答案 2 :(得分:5)
但是如果我将struct-values放在一个列表中并返回它会发生什么?这些元素幸存下来。
从技术上讲,添加到“列表”的值不是相同的值,它们是基于值的副本。例如,如果您修改原件,则不会将这些更改带到列表中的副本。此外,'List'返回指定索引处的值的副本。这意味着如果结构是可变的并且你修改了从'List'返回的值,那么List<t>
中的值将保持不变。数组不是这种情况,因为数组索引提供了对实际变量的访问。
答案 3 :(得分:1)
有时可以在堆上分配所有类型。除此之外,堆/堆栈是CLR的实现细节,而不是C#规范,所以你不应该依赖这些东西。有关此主题的精彩博文,请参阅here。
答案 4 :(得分:1)
从我记忆中......
值类型的位置取决于它们的声明位置。在堆栈帧中执行方法之后,方法变量被分配,存储在堆栈中并被移除。声明为引用类型一部分的值类型存储在封闭类型结构中的堆上。
如果我错了,请告诉我!
答案 5 :(得分:1)
struct类型的存储位置(变量,字段,参数,数组槽等)包含struct的公共和私有字段。如果该存储位置在堆栈上,则struct的字段将在堆栈上。如果它在另一个类或结构中,则struct的字段将作为其他类或结构实例的一部分存储在中。
类类型的存储位置将引用保存到一个完整的类对象,该对象始终(1)存储在与保存引用的存储位置完全分开的某个位置,或者(2)该存储位置为字段的类对象。