public class MyClass
{
public int Age;
public int ID;
}
public void MyMethod()
{
MyClass m = new MyClass();
int newID;
}
据我了解,以下情况属实:
这是我的问题:
从逻辑上讲,我认为类中的值类型会在堆中,但我不确定是否必须将它们装箱才能到达那里。
编辑:
有关此主题的建议阅读材料:
答案 0 :(得分:9)
类的值类型值与托管堆中的对象实例一起生成。方法的线程堆栈仅在方法的持续时间内存在;如果值仅存在于该堆栈中,该值如何保持不变?
托管堆中的类'对象大小是其值类型字段,引用类型指针和其他CLR开销变量(如同步块索引)的总和。当一个值为一个对象的value-type字段赋值时,CLR会将该值复制到该特定元素字段的对象内分配的空间。
以一个带有单个字段的简单类为例。
public class EmbeddedValues
{
public int NumberField;
}
用它,一个简单的测试类。
public class EmbeddedTest
{
public void TestEmbeddedValues()
{
EmbeddedValues valueContainer = new EmbeddedValues();
valueContainer.NumberField = 20;
int publicField = valueContainer.NumberField;
}
}
如果您使用.NET Framework SDK提供的MSIL反汇编程序来查看EmbeddedTest.TestEmbeddedValues()的IL代码
.method public hidebysig instance void TestEmbeddedValues() cil managed
{
// Code size 23 (0x17)
.maxstack 2
.locals init ([0] class soapextensions.EmbeddedValues valueContainer,
[1] int32 publicField)
IL_0000: nop
IL_0001: newobj instance void soapextensions.EmbeddedValues::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.s 20
IL_000a: stfld int32 soapextensions.EmbeddedValues::NumberField
IL_000f: ldloc.0
IL_0010: ldfld int32 soapextensions.EmbeddedValues::NumberField
IL_0015: stloc.1
IL_0016: ret
} // end of method EmbeddedTest::TestEmbeddedValues
请注意,CLR被告知 stfld 堆栈中加载的值“20”到加载的EmbeddValues的NumberField字段位置,直接进入托管堆。类似地,在检索值时,它使用 ldfld 指令将该托管堆位置的值直接复制到线程堆栈中。这些类型的操作不会发生框/拆箱。
答案 1 :(得分:2)
答案 2 :(得分:2)
我见过的最好的资源是杰弗里里希特的书籍CLR by C#。如果您进行任何.NET开发,那么非常值得一读。基于该文本,我的理解是引用类型中的值类型确实存在于父对象中嵌入的堆中。堆上的引用类型始终。 拳击和拆箱不对称。拳击比拆箱更重要。 Boxing 将要求将值类型的内容从堆栈复制到堆。根据这种情况发生的频率,有一个结构而不是一个类可能没有意义。如果您有一些性能关键代码并且您不确定是否正在进行装箱和拆箱,请使用工具检查方法的IL代码。你会在IL中看到单词box和unbox。就个人而言,我会衡量我的代码的性能,然后再看看这是否是一个担心的候选人。在你的情况下,我不认为这将是一个如此关键的问题。每次在引用类型中访问此值类型时,您都不必从堆栈复制到堆(框)。那种情况是拳击成为一个更有意义的问题。
答案 3 :(得分:2)
引用类型(RT)始终生成在堆上分配的实例。 相反,值类型(VT)取决于上下文 - 如果本地var是VT,则CLR在堆栈上分配内存。如果类中的字段是VT的成员,则CLR为实例分配内存,作为声明字段的对象/类型布局的一部分。
答案#2:不会。只有通过对象参考/接口指针访问结构时才会发生拳击。 obInstance.VT_typedfield 不会包装。
RT变量包含它引用的对象的地址。 2 RT var可以指向同一个对象。 相反,VT变量本身就是实例。 2 VT var不能指向同一个对象(struct)
答案3:Don Box的Essential .net / Jeffrey Richter通过C#的CLR。我有一份前者的副本......虽然后者可能会更新.Net修订
答案 4 :(得分:1)
对象中的值类型是否存在于堆栈或堆上?
在堆上。它们是对象足迹分配的一部分,就像保持引用的指针一样。
对象中的装箱/取消装箱值类型是否值得关注?
这里没有拳击。
这个主题是否有任何详细但可理解的资源?
给Richter的书+1票。
答案 5 :(得分:0)
结构类型的变量或其他存储位置是该类型的公共和私有实例字段的聚合。给定
struct Foo {public int x,y; int z;}
声明Foo bar;
将导致bar.x
,bar.y
和bar.z
存储在bar
将要存储的任何位置。从存储布局的角度来看,将bar
这样的声明添加到类中将等同于添加三个int
字段。实际上,如果除了访问其字段之外从未对bar
执行任何操作,bar
的字段的行为与三个字段bar_x
,bar_y
和{{1 [访问最后一个将需要使用bar_cantaccessthis_z
而不是访问其字段,但无论它是否实际用于任何事物都会占用空间。
将结构类型存储位置识别为字段聚合是理解结构的第一步。试图将它们视为持有某种对象可能看起来“更简单”,但与实际工作方式不符。