简体   繁体   English

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

[英]Boxing / Unboxing Nullable Types - Why this implementation?

Extract from CLR via C# on Boxing / Unboxing value types ... 通过C#在Boxing / Unboxing值类型上从CLR中提取...

On Boxing: If the nullable instance is not null , the CLR takes the value out of the nullable instance and boxes it. 在Boxing上:如果可空实例不为null ,则CLR将该值从可空实例中取出并将其装箱。 In other words a Nullable < Int32 > with a value of 5 is boxed into a boxed-Int32 with a value of 5. 换句话说,值为5Nullable <Int32>被装入一个值为5boxed-Int32

On Unboxing: Unboxing is simply the act of obtaining a reference to the unboxed portion of a boxed object. 在取消装箱:取消装箱只是获取对盒装对象的未装箱部分的引用的行为。 The problem is that a boxed value type cannot be simply unboxed into a nullable version of that value type because the boxed value doesn't have the boolean hasValue field in it. 问题是盒装值类型不能简单地拆箱成该值类型的可空版本,因为盒装值中没有boolean hasValue字段。 So, when unboxing a value type into a nullable version, the CLR must allocate a Nullable < T > object, initialize the hasValue field to true , and set the value field to the same value that is in the boxed value type. 因此,当将值类型拆箱到可空版本时,CLR必须分配Nullable <T>对象,将hasValue字段初始化为true ,并将value字段设置为盒装值类型中的相同值。 This impacts your application performance (memory allocation during unboxing). 这会影响您的应用程序性能(拆箱期间的内存分配)。

Why did the CLR team go through so much trouble for Nullable types ? 为什么CLR团队为Nullable类型经历了这么多麻烦? Why was it not simply boxed into a Nullable < Int32 > in the first place ? 为什么不首先将它装入Nullable <Int32>?

I remember this behavior was kind of last minute change. 我记得这种行为是最后一刻的变化。 In early betas of .NET 2.0, Nullable<T> was a "normal" value type. 在.NET 2.0的早期测试版中, Nullable<T>是一种“正常”值类型。 Boxing a null valued int? 拳击nullint? turned it into a boxed int? 把它变成盒装的int? with a boolean flag. 带有布尔标志。 I think the reason they decided to choose the current approach is consistency. 我认为他们决定选择当前方法的原因是一致性。 Say: 说:

int? test = null;
object obj = test;
if (test != null)
   Console.WriteLine("test is not null");
if (obj != null)
   Console.WriteLine("obj is not null"); 

In the former approach (box null -> boxed Nullable<T> ), you wouldn't get "test is not null" but you'd get "object is not null" which is weird. 在前一种方法(box null - > boxed Nullable<T> )中,你不会得到“test is not null”但是你得到“object is not null”这很奇怪。

Additionally, if they had boxed a nullable value to a boxed-Nullable<T> : 另外,如果他们将一个可以为空的值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. 
}

Beside that, I believe the current behavior makes perfect sense for scenarios like nullable database values (think SQL-CLR...) 除此之外,我相信当前的行为对于可空数据库值这样的场景非常有意义(想想SQL-CLR ...)


Clarification: 澄清:

The whole point of providing nullable types is to make it easy to deal with variables that have no meaningful value. 提供可空类型的重点是使处理没有意义值的变量变得容易。 They didn't want to provide two distinct, unrelated types. 他们不想提供两种截然不同的类型。 An int? 一个int? should behaved more or less like a simple int . 应该表现得或多或少像一个简单的int That's why C# provides lifted operators. 这就是为什么C#提供了提升的运营商。

So, when unboxing a value type into a nullable version, the CLR must allocate a Nullable<T> object, initialize the hasValue field to true, and set the value field to the same value that is in the boxed value type. 因此,当将值类型拆箱到可空版本时,CLR必须分配Nullable<T>对象,将hasValue字段初始化为true,并将value字段设置为盒装值类型中的相同值。 This impacts your application performance (memory allocation during unboxing). 这会影响您的应用程序性能(拆箱期间的内存分配)。

This is not true. 这不是真的。 The CLR would have to allocates memory on stack to hold the variable whether or not it's nullable. CLR必须在堆栈上分配内存以保存变量,无论它是否可为空。 There's not a performance issue to allocate space for an extra boolean variable. 为额外的布尔变量分配空间没有性能问题。

I think it makes sense to box a null value to a null reference. 我认为将空值包装到空引用是有意义的。 Having a boxed value saying "I know I would be an Int32 if I had a value, but I don't" seems unintuitive to me. 有一个盒装价值说“我知道如果我有价值,我会是一个Int32 ,但我不是”对我来说似乎不直观。 Better to go from the value type version of "not a value" (a value with HasValue as false) to the reference type version of "not a value" (a null reference). 最好从“非值”的值类型版本( HasValue为false的值)转换为“非值”的引用类型版本(空引用)。

I believe this change was made on the feedback of the community, btw. 我相信这种变化是基于社区的反馈,顺便说一句。

This also allows an interesting use of as even for value types: 这也使得一个有趣的使用as甚至值类型:

object mightBeADouble = GetMyValue();

double? unboxed = mightBeADouble as double?;
if (unboxed != null)
{
    ...
}

This is more consistent with the way "uncertain conversions" are handled with reference types, than the previous: 这与使用引用类型处理“不确定转换”的方式更加一致,而不是之前的:

object mightBeADouble = GetMyValue();

if (mightBeADouble is double)
{
    double unboxed = (double) mightBeADouble;
    ...
}

(It may also perform better, as there's only a single execution time type check.) (它也可能表现更好,因为只有一次执行时间类型检查。)

A thing that you gain via this behavior is that the boxed version implements all interfaces supported by the underlying type. 通过此行为获得的一件事是盒装版本实现了底层类型支持的所有接口。 (The goal is to make Nullable<int> appear the same as int for all practical purposes.) Boxing to a boxed-Nullable<int> instead of a boxed-int would prevent this behavior. (目的是使Nullable<int>出于所有实际目的看起来与int相同。)对boxed-Nullable<int>而不是boxed-int进行boxed-int会阻止此行为。

From the MSDN Page, 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();

Also getting the int from a boxed version of a Nullable<int> is straightforward - Usually you can't unbox to a type other than the original src type. Nullable<int>的盒装版本获取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);

Here you do not have to know if the original version was an int or a Nullable version of it. 在这里,您不必知道原始版本是int还是Nullable版本。 (+ you get some perf too ; save space of storing the the hasValue boolean as well in the boxed object) (+你也得到一些性能;在盒装对象中保存存储hasValue布尔值的空间)

I guess that is basically what it does. 我想这基本上就是它的作用。 The description given includes your suggestion (ie boxing into a Nullable<T> ). 给出的描述包括你的建议(即装入Nullable<T> )。

The extra is that it sets the hasValue field after boxing. 额外的是它在装箱后设置hasValue字段。

I would posit that the reason for the behavior stems from the behavior of Object.Equals, most notably the fact that if the first object is null and the second object is not, Object.Equals returns false rather than call the Equals method on the second object. 我认为行为的原因源于Object.Equals的行为,最值得注意的是,如果第一个对象为null而第二个对象不是,则Object.Equals返回false而不是在第二个上调用Equals方法宾语。

If Object.Equals would have called the Equals method on the second object in the case where the first object was null but the second was not, then an object which was null-valued Nullable<T> could have returned True when compared to null. 如果在第一个对象为空但第二个对象不为的情况下,Object.Equals会在第二个对象上调用Equals方法,那么与null相比,null值Nullable <T>的对象可能返回True。 Personally, I think the proper remedy would have been to make the HasValue property of a Nullable<T> have nothing to do with the concept of a null reference. 就个人而言,我认为正确的补救方法是使Nullable <T>的HasValue属性与null引用的概念无关。 With regard to the overhead involved with storing a boolean flag on the heap, one could have provided that for every type Nullable<T> there would a be a static boxed empty version, and then provide that unboxing the static boxed empty copy would yield an empty Nullable<T>, and unboxing any other instance would yield a populated one. 关于在堆上存储布尔标志所涉及的开销,可以为每个类型Nullable <T>提供一个静态的盒装空版本,然后提供取消装箱的静态盒装空副本将产生一个清空Nullable <T>,并取消装箱任何其他实例将产生一个填充的实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM