拳击/拆箱可空类型 - 为什么要实施?

Pre*_*ets 22 .net c# clr nullable

通过C#在Boxing/Unboxing值类型上从CLR中提取...

在Boxing上:如果可空实例不为null,则CLR将该值从可空实例中取出并将其装箱.换句话说,值为5Nullable <Int32>被装入一个值为5boxed-Int32.

在取消装箱:取消装箱只是获取对盒装对象的未装箱部分的引用的行为.问题是盒装值类型不能简单地拆箱成该值类型的可空版本,因为盒装值中没有boolean hasValue字段.因此,当将值类型拆箱到可空版本时,CLR必须分配Nullable <T>对象,将hasValue字段初始化为true,并将value字段设置为盒装值类型中的相同值.这会影响您的应用程序性能(拆箱期间的内存分配).

为什么CLR团队为Nullable类型经历了这么多麻烦?为什么不首先将它装入Nullable <Int32>?

Meh*_*ari 23

我记得这种行为是最后一刻的变化.在.NET 2.0的早期版本中,它Nullable<T>是一种"正常"值类型.拳击一个null有价值的int?把它变成一个int?带有布尔标志的盒子.我认为他们决定选择当前方法的原因是一致性.说:

int? test = null;
object obj = test;
if (test != null)
   Console.WriteLine("test is not null");
if (obj != null)
   Console.WriteLine("obj is not null"); 
Run Code Online (Sandbox Code Playgroud)

在前一种方法(box null- > boxed Nullable<T>)中,你不会得到"test is not null"但你得到"object is not null"这很奇怪.

此外,如果他们将可以为空的值装箱boxed-Nullable<T>:

int? val = 42;
object obj = val;

if (obj != null) {
   // Our object is not null, so intuitively it's an `int` value:
   int x = (int)obj; // ...but this would have failed. 
}
Run Code Online (Sandbox Code Playgroud)

除此之外,我相信当前的行为对于可空数据库值这样的场景非常有意义(想想SQL-CLR ...)


澄清:

提供可空类型的重点是使处理没有意义值的变量变得容易.他们不想提供两种截然不同的类型.一个int?应该表现或多或少像一个简单的int.这就是为什么C#提供了提升的运营商.

因此,当将值类型拆箱到可空版本时,CLR必须分配一个Nullable<T>对象,将hasValue字段初始化为true,并将value字段设置为盒装值类型中的相同值.这会影响您的应用程序性能(拆箱期间的内存分配).

这不是真的.CLR必须在堆栈上分配内存以保存变量,无论它是否可为空.为额外的布尔变量分配空间没有性能问题.

  • Preets:如果他们已经这样做了,你就不能将它直接拆箱到`int`. (2认同)
  • 虽然我可以理解处理器缓存参数(我不认为它在大多数情况下都很重要),但我不确定GC压力参数.无论是box和int还是将Nullable <int>包装起来,GC仍然将其作为单个块处理.创建它只是一个分配(GC几乎没有),删除意味着标记/扫描/紧凑仍然会标记,扫描和压缩块,无论如何.我看不出GC负载有什么不同......只是分配块大小的差异.我认为问题的关键在于Mehrdad所说:"unbox直接转向int". (2认同)

Jon*_*eet 9

我认为将空值包装到空引用是有意义的.有一个盒装价值说"我知道Int32如果我有价值,我会是一个,但我不是",这对我来说似乎不直观.最好从"非值"(值为HasValuefalse)的值类型版本转换为"非值" 的引用类型版本(空引用).

我相信这种变化是基于社区的反馈,顺便说一句.

这也允许as对值类型进行有趣的使用:

object mightBeADouble = GetMyValue();

double? unboxed = mightBeADouble as double?;
if (unboxed != null)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

这与使用引用类型处理"不确定转换"的方式更加一致,而不是之前的:

object mightBeADouble = GetMyValue();

if (mightBeADouble is double)
{
    double unboxed = (double) mightBeADouble;
    ...
}
Run Code Online (Sandbox Code Playgroud)

(它也可能表现更好,因为只有一次执行时间类型检查.)


Gis*_*shu 5

通过此行为获得的一件事是盒装版本实现了底层类型支持的所有接口.(目标是使Nullable<int>出现与int所有实际目的相同.)拳击到a boxed-Nullable<int>而不是a boxed-int会阻止这种行为.

MSDN页面,

double? d = 44.4;
  object iBoxed = d;
  // Access IConvertible interface implemented by double.
  IConvertible ic = (IConvertible)iBoxed;
  int i = ic.ToInt32(null);
  string str = ic.ToString();
Run Code Online (Sandbox Code Playgroud)

从盒装版本获取int也Nullable<int>很简单 - 通常你不能取消装入原始src类型以外的类型.

float f = 1.5f;
object boxed_float = f;
int int_value = (int) boxed_float; // will blow up. Cannot unbox a float to an int, you *must* unbox to a float first.

float? nullableFloat = 1.4f;
boxed_float = nullableFloat;
float fValue = (float) boxed_float;  // can unbox a float? to a float    Console.WriteLine(fValue);
Run Code Online (Sandbox Code Playgroud)

在这里,您不必知道原始版本是int还是Nullable版本.(+你也得到一些性能;节省存储hasValue布尔值的空间以及盒装对象)