dra*_*vic 10 .net c# value-type reference-type
当operator new()与引用类型一起使用时,实例的空间在堆上分配,引用变量本身放在堆栈上.除此之外,在堆上分配的引用类型实例中的所有内容都将被清零.
例如,这是一个类:
class Person
{
public int id;
public string name;
}
Run Code Online (Sandbox Code Playgroud)
在以下代码中:
class PersonDemo
{
static void Main()
{
Person p = new Person();
Console.WriteLine("id: {0} name: {1}", p.id, p.name);
}
}
Run Code Online (Sandbox Code Playgroud)
p变量在堆栈上,并且Person(所有它的成员)的创建实例都在堆上.p.id会是0 和p.name将会null.这将是这种情况,因为堆上分配的所有内容都已清零.
现在我感到困惑的是,如果我使用带new运算符的值类型.例如,考虑以下结构:
struct Date
{
public int year;
public int month;
public int day;
}
class DateDemo
{
static void Main()
{
Date someDate;
someDate= new Date();
Console.WriteLine("someDate is: {0}/{1}/{2}",
someDate.month, someDate.day, someDate.year);
}
}
Run Code Online (Sandbox Code Playgroud)
现在我想知道main的以下几行是做什么的:
Date someDate;
someDate= new Date();
Run Code Online (Sandbox Code Playgroud)
在第一行中,someDate变量在堆栈上分配.准确地说是12个字节.
我的问题是第二行会发生什么?怎么operator new()办?它只是将Date结构的成员清零,还是在堆上分配空间?一方面我不希望new在堆上分配空间,当然因为在第一行中已经在堆栈中为结构实例分配了内存.另一方面,我希望new在堆上分配空间并返回该空间的地址,因为这是new应该做的.也许这是因为我来自C++背景.
然而,如果答案是:"何时new与值类型一起使用,它只会将对象的成员清零",而不是new运算符的含义不一致,因为:
new值类型时,它只会将堆栈中的对象成员清零new引用类型时,它会在堆上为实例分配内存并使其成员为zerous-out提前谢谢,
干杯
Eri*_*ert 20
首先让我纠正你的错误.
当operator new()与引用类型一起使用时,实例的空间在堆上分配,引用变量本身放在堆栈上.
作为"new"结果的引用是值,而不是变量.该值指的是存储位置.
该引用当然是在CPU寄存器中返回的.是否将该CPU寄存器的内容复制到调用堆栈是抖动优化器决定的问题.它不需要永远存在于堆栈中; 它可以永久存在于寄存器中,或者可以直接从寄存器复制到托管堆,或者,在不安全的代码中,它可以直接复制到非托管内存.
堆栈是一个实现细节.除非您查看jitted代码,否则您不知道何时使用堆栈.
p变量在堆栈上,并且创建的Person实例(所有的memebers)都在堆上.p.id为0,p.name为null.
正确,但当然,如果抖动如此决定,p可以实现为寄存器.如果有可用的寄存器,则无需使用堆栈.
你似乎对这个堆栈正在使用的想法非常感兴趣.抖动可能有大量的寄存器,这些寄存器可能非常大.
我来自C++背景.
啊,这就解释了为什么你如此挂在这堆栈和堆上的东西.学会不要担心它.我们设计了一个托管内存环境,只要需要,它们就会存在.管理者是否选择使用堆栈,堆或寄存器来有效地管理内存取决于它.
在第一行中,someDate变量在堆栈上分配.准确地说是12个字节.
让我们假设为了论证,这个12字节结构被分配在堆栈上.似乎合理.
我的问题是第二行会发生什么?operator new()做什么?它只是将Date结构的成员清零,还是在堆上分配空间?
这个问题预示着错误的二分法,因此无法按照说明回答.这个问题提出了两个或两个选择,两者都不一定正确.
一方面我不希望new在堆上分配空间,当然因为在第一行中已经在堆栈中为结构实例分配了内存.
正确的结论,似是而非的推理.不执行堆分配,因为编译器知道此操作的任何部分都不需要长期存储.这就是堆的用途; 当编译器确定给定变量可能比当前方法激活时间更长时,它会生成代码,该代码在长期"堆"存储上为该变量分配存储.如果它确定变量肯定具有短寿命,那么它使用堆栈(或寄存器)作为优化.
另一方面,我希望new在堆上分配空间并返回该空间的地址,因为这是新的应该做的.
不正确."new"不保证堆已分配.相反,"new"保证在置零内存上调用构造函数.
让我们回到你的问题:
它只是将Date结构的成员清零,还是在堆上分配空间?
我们知道它不会在堆上分配空间.它是否将日期结构的成员归零?
这是一个复杂的问题.规范说明当你说时会发生什么
someDate = new Date();
Run Code Online (Sandbox Code Playgroud)
现在,实际上会发生什么?您完全有权注意到无法判断是否已分配,初始化和复制新堆栈空间,或者是否初始化了"旧"堆栈空间.
答案是,在编译器推断用户无法注意到现有堆栈空间发生变异的情况下,现有的堆栈空间会发生变化,并且会省略额外的分配和后续复制.
如果编译器无法推断出这种情况,则会创建一个临时堆栈槽,初始化为零,由构造函数构造,变异,然后将结果值复制到变量中.这可以确保如果构造函数抛出异常,则无法在变量中观察到不一致的状态.
有关此问题的更多详细信息及其编译器的分析,请参阅我关于此主题的文章.
http://blogs.msdn.com/b/ericlippert/archive/2010/10/11/debunking-another-myth-about-value-types.aspx
好的,这是一个简单的:
class Program
{
static void Main(string[] args)
{
DateTime dateTime = new DateTime();
dateTime = new DateTime();
Console.Read();
}
}
Run Code Online (Sandbox Code Playgroud)
编译为此IL代码:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 24 (0x18)
.maxstack 1
.locals init ([0] valuetype [mscorlib]System.DateTime dateTime)
IL_0000: nop
IL_0001: ldloca.s dateTime
IL_0003: initobj [mscorlib]System.DateTime
IL_0009: ldloca.s dateTime
IL_000b: initobj [mscorlib]System.DateTime
IL_0011: call int32 [mscorlib]System.Console::Read()
IL_0016: pop
IL_0017: ret
} // end of method Program::Main
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,CLR将使用相同的局部变量来存储新值类型,尽管它将再次运行构造函数 - 这很可能只是将内存归零.我们看不出initobj是什么,这是一个CLR实现.
现实是,作为埃里克利珀解释在这里,有关于在栈上被分配值类型没有这样的一般规则.这完全取决于CLR的实现.
| 归档时间: |
|
| 查看次数: |
1777 次 |
| 最近记录: |