我只是想知道下两个陈述之间有什么区别:
导致NullReferenceException - 没关系。
object test1 = default(int?);
object result = test1.ToString();
返回一个空字符串“”,为什么?
object test2 = default(int?).ToString();
这与2相同。
int? test3 = null;
if (test3.ToString() == string.Empty) //returns true
{
Console.WriteLine("int? = String.Empty, isn't it strange?").
}
只是为了好玩 - 我可以证明bool可以等于int值(嗯怎么样?bool只能是false,或者是true而int永远不会那样)。
if (default(int?).ToString() == default(bool?).ToString()) //returns true because both are empty strings
{
Console.WriteLine("int = bool");
}
注意: default(int?)返回null。
答案 0 :(得分:29)
int?
是Nullable<int>
。 Nullable
是一个 struct ,它使其成为值类型,而不是引用类型。 test3
实际上未设置为null
。为其分配null
实际上会导致创建一个新结构,该结构只将该结构的HasValue
字段设置为false
而不是true
。 (这种隐式转换需要特殊的编译器支持;因此你不能拥有自己的MyNullable<T>
类型来执行此操作。)
由于可空对象具有实际值,因此它可以调用ToString
并提供有意义的值,在本例中为空字符串。
当您将该可空值放入object
变量中时,它会导致实际 null
值存储在该变量中,这就是您获得的原因在调用方法时使用NRE。
当一个可以为空的值类型被装箱时,它不会包含可空值类型;相反,如果它没有值,它会将null
分配给对象,如果它有一个值,那么该基础值将被解包然后装箱。 (此行为需要来自运行时的特殊支持,这是您无法创建自己的MyNullable<T>
类型的另一个原因。)
答案 1 :(得分:1)
在C中,使用令牌.
来访问左操作数的成员,使用->
来访问左操作数持有指针(引用)的事物的成员。如果->
的左侧操作数为空,则发生空引用。在C#中,.
在应用于值类型时具有前者的含义,后者在应用于类类型时具有意义;在类类型变量上使用C#中的.
将触发NullReferenceException
。
.NET类型Nullable<T>
是一个双字段结构,它包含指示它是否具有有意义(非空)值的布尔字段,以及值类型T
的字段来说明这是什么value是(如果不为null)。虽然编译器允许将Nullable<T>
指定为null或将其与null进行比较,但前一种情况下的null
实际上只是默认实例的简写(其中“has-value”标志为false,并且value字段保存类型T的默认值,并且与null的比较实际上只是检查HasValue
属性(它依次查看Boolean
字段)的简写。在{例如}上调用ToString
Nullable<int>
“null”在语义上与在包含Boolean
和Int32
且值False
和{{1}的任何其他结构上调用它没有区别}}, 分别。编译器有几种方法可以“特殊”处理可空类型,但默认值0
包含Nullable<int>
,而不是空引用。
请注意,第一个示例因为框架应用于可空类型的“特殊”规则之一而抛出。将值类型转换为[False, 0]
或其他引用类型通常会指示运行时创建一个对象,该对象将作为类对象运行,其中字段和成员与结构的对象匹配,并返回对该新对象的引用 - 一个进程被称为“拳击”。因为可空类型的设计者希望允许代码说Object
而不是(IMHO更清晰)if (NullableThing == null)
,所以实现了运行时,以便装入if (!NullableThing.HasValue)
字段为false的可空字段产生空引用而不是对HasValue
的默认值实例的引用。虽然我认为有些情况下某些值类型使用不同的box和unbox方法是有意义的,但可空类型是唯一可以使用除正常装箱之外的其他类型。