内存中可存储的可空类型?

时间:2010-05-19 12:52:16

标签: c# .net nullable

这可能是关于nullable types的问题的后续问题。

哪些可以为空的值类型(int? ...)存储在内存中?首先我认为这很清楚,因为Nullable<T>是结构,而那些是值类型。然后我找到了Jon Skeet的文章“Memory in .NET”,其中说:

  

请注意,值类型变量可以   从来没有null值 - 它   没有任何意义,因为null是一个   参考类型概念,意思是“   此引用类型变量的值   不是对任何对象的引用   所有”。

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

4 个答案:

答案 0 :(得分:71)

首先,Nullable<int>只是一个简写:

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

加上所有构造函数,访问器等。这就是全部 - 可以为空的int是一个普通的int加上一个标志,表明int是否为null。剩下的就是将“null”视为有效值的编译器魔法;所有带有可空类型的“null”都会使你成为那些标志设置为false的结构之一。

所以现在我们已经解决了这个问题,你的问题是“他们在记忆中的位置”?它们与内存中任何其他结构相同的位置:运行时和编译器相信在给定内存生命周期的最佳位置。

大多数结构都在堆上。任何告诉你“结构总是在堆栈上”的人实际上并不知道他们在谈论什么;我们的文档没有说明,这不是真的。结构仅在临时内存池(即“堆栈”)上,当它们是局部变量或临时变量时,并且局部变量不是封闭的匿名方法或lambda的外部变量,并且局部变量不在迭代器中块。在我们的实现中,所有其他结构都在堆上。

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

我不得不问:你为什么关心? CLR代表您管理内存。你为什么关心可空类型的去处?他们去了他们住的地方足够长的时间对你有用;你不必担心。

答案 1 :(得分:7)

请参阅Eric关于结构存储位置的评论。它比我发布的内容更彻底(也更正确)。

它的工作方式是nullable有一个布尔值,表示该值是否已设置。从技术上讲,你是正确的nullable(int,bool)等实际上不是null。它有一个默认值。它只是现在可空类型有一个布尔值你可以检查并查看该值是否已设置或它只是它的默认值。在为=(及其值)分配==时,会覆盖null和{{1}}以设置适当的值

MSDN链接到Nullables

另一个描述nullables如何工作的链接:
http://www.markzhou.com/blog/post/2010/01/27/Why-can-assign-e2809cnulle2809d-to-nullable-types.aspx

答案 2 :(得分:3)

Nullables只假装为null:

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);

答案 3 :(得分:2)

除了Kevin的回答:编译器知道Nullable类型并将==null检查更改为对“.HasValue”的调用。