2010-05-19 52 views
28

这可能是后续问题nullable types内存中的哪些内存是可空类型?

存储器中存储了哪些可为空的值类型(int? ...)?首先,我认为这已经足够清晰了,因为Nullable<T>是结构体,它们是值类型。然后我发现乔恩斯基特的文章“Memory in .NET”,其中说:

注意,值类型变量可以 永远不会有一个null值 - 这 将没有任何意义,因为空是一个 引用类型概念,意思是“此参考类型变量 的值 不是对所有对象的引用” 全部“。

阅读本声明后,我有点困惑。所以我们假设我有int? a = null;。由于int通常是一个值类型,它是否以栈方式存储在结构Nullable<T>内(我使用“正常”,因为我不知道当值类型变为空时会发生什么)?或者其他任何事情发生在这里 - 也许堆在一起?

回答

67

首先,Nullable<int>仅仅是类似的简写:

struct Nullable<T> 
{ 
    bool hasValue; 
    T value; 
} 

加所有构造函数,访问器等等。就是这样 - 一个可为空的int是一个普通的int和一个表示int是否为null的标志。其余的部分是编译器魔术,它将“null”视为有效值;所有“空”与可为空的类型都会使您成为标志设置为false的结构之一。

所以现在我们已经有了这个方法,你的问题是“他们在记忆中的位置”?它们与任何其他结构在内存中的位置相同:运行时和编译器相信在给定内存使用期限的情况下,它们是最佳位置。

大多数结构堆积如山。任何告诉你“结构总是堆积如山”的人实际上并不知道他们在说什么;我们的文档没有说明,也不是这样。当它们是局部变量或临时对象时,结构只能在临时内存池上,即“堆栈”上进行操作,并且局部变量不会关闭 - 匿名方法或lambda的外部变量,并且局部变量不在迭代器中块。所有其他结构都在我们的实施中堆积如山。

另请注意,CLI的实现无需使用“堆栈”来创建其临时池。例如,经典的JScript内存管理器将其临时池存储在堆上。 (虽然JScript运行时引擎当然不是CLI的实现,但我只是指出可以设计一个托管的运行时引擎,它不会将任何用户数据放在“堆栈”上)。从逻辑上讲,它是一个堆栈数据结构,但该数据结构不存储在“堆栈”上,它只是堆中分配的堆栈结构。

我不得不问:你为什么在意? CLR代表您管理内存。为什么你关心可空类型在哪里?他们走到他们住的地方足够长,对你有用;你不必担心这一点。

+6

来自我的非常容易的+1。谢谢你用尽了答案。我很在乎,因为我很好奇。我想(可能天真地说)知道在我的代码背后发生了什么是有价值的信息。我不想最终编写一些神奇的东西 - 什么神奇的东西。 – 2010-05-19 14:41:04

+2

@Ondrej,如果您认为Nullable编译器支持是魔术,请尝试使用包含闭包的lambda表达式使用yield返回来编写IEnumerable方法。在ildasm中打开生成的程序,看看编译器为你做了什么。 – 2010-05-19 14:48:53

+0

我一般都在讲。并不是说我实际上认为编程中的一切都是神奇的。我知道最有可能比这更难以理解的代码段。 – 2010-05-19 15:47:00

7

请参阅Eric关于存储结构的位置的注释。它比我发布的更彻底(更正确)。

它的工作方式是nullable有一个布尔值,表示值是否已设置。从技术上讲,你是正确的可空(int,布尔)等实际上不是null。它有一个默认值。只是现在可空类型有一个布尔类型,你可以检查它是否被设置,或者它只是它的默认值。
http://www.markzhou.com/blog/post/2010/01/27/Why-can-assign-e2809cnulle2809d-to-nullable-types.aspx

+4

结构并不总是存储在堆栈中。一个正确的说法是:“结构类型的局部变量未关闭匿名方法或lambda表达式的外部变量,并且不在迭代器块中存储在桌面CLR的Microsoft实现的堆栈中。”没有要求存在称为“堆栈”的数据结构或者存储所有结构。作为类的字段或迭代器块中的局部变量或外部变量的结构存储在堆中。 – 2010-05-19 14:11:56

+0

一如既往,感谢您的澄清。 – kemiller2002 2010-05-19 14:22:27

2

除了:所述===分配时null(和的值),以它

MSDN连结Nullables

描述nullables如何工作的另一链路是重写,以设置适当的值凯文的答案是:编译器知道可空类型并将==null检查更改为对“.HasValue”的调用。

3

Nullables只能假装为空:

int? a = null; 
Debug.Assert((a == null) == (!a.HasValue)); 

编译器是这场游戏的同谋。这样做的一个有趣的结果如下:

Nullable<double> b = new Nullable<double>(); 
Debug.Assert(b == null); //I'm null upon construction! 

他们还得到特殊的拳击支持:

int? c = null; 
Object d = c; 
Debug.Assert(d == null); 
Debug.Assert(!c.HasValue); 
+0

你忘了所有的魔法(你无法用运算符重载来模拟):操作员提升。 – 2010-05-20 10:58:50