selectedItem
有两个字段:
诠释? _成本
字符串 _serialNumber
在此示例中,selectedItem
的 _cost
和 _serialNumber
都为空。我正在通过它们的属性阅读 selectedItem
的字段,并用它们的值填充文本框,当...
TextBox1.Text = selectedItem.Cost.ToString(); //no error
TextBox2.Text = selectedItem.SerialNumber.ToString(); //error
我知道 SerialNumber.ToString()
是多余的(因为它已经是一个字符串),但我不明白为什么这会导致此异常:
可空对象必须有一个值。
诠释? _cost 可以为空,并且没有值,但它没有给我例外。
string _serialNumber 可以为空,并且没有值,但它确实给了我例外。
这个question涉及到它,这家伙本质上是在问同样的事情,但是没有指定答案,也没有解释为什么可以为空的int
?例如,我可以在可为空的 int 上使用 .ToString()
而不是在空字符串上吗?
MessageBox.Show
和 String.Concat
如何与 null
字符串一起工作的。
int?
是一种值类型,称为 Nullable<T>
,对空值有特殊处理; string
是一种引用类型(虽然有点奇怪),它实际上可以有 null
的值。 int?
总是有价值的,它只是有一种特殊的方式来表达“我现在表现得像 null
”。
因为 string
类型的 null
确实没有指向任何内容,所以内存中没有任何对象。
但是 int?
类型(可为空)即使值设置为 null
仍然指向某个对象。
如果您阅读 Jeffrey Richter 的“CLR via C#”,您会发现可空类型只是具有一些封装逻辑的常见类型的外观类,以便更方便地使用 DB null。
检查 msdn 了解可空类型。
Nullable<int>
是 struct
,不能真正为空。因此,对“null”结构的方法调用仍然有效。
有一些“编译器魔法”使 _cost == null
成为一个有效的表达式。
null
到 Nullable<T>
的隐式转换将要求您能够从表达式“null
”中推断出一个类型,而您不能这样做,因为它没有没有。编译器实质上将 Nullable<T> foo = null
转换为 Nullable<T> foo = default(Nullable<T>)
并将 foo == null
转换为 !foo.HasValue
。
int?
实际上本身并不是一个对象,而是一个 Nullable<int>
对象。
因此,当您声明 int? _Cost
时,您实际上是在声明 Nullable<int> _Cost
并且 _Cost.Value
的属性是 undefined
而不是 _Cost
对象本身。
它实际上是一种语法糖,可以轻松使用 int、bool 或 decimal 等不可为空的类型。
根据MSDN:
语法 T?是 System.Nullable
_Cost
为空(即当 _Cost.HasValue
为假时)时,_Cost.Value
未定义
_Cost
不可能是 null
,因为它不是引用类型。
Nullable 实际上是一个公开两个属性的结构:HasValue 和 Value。如果你这样做,你会得到你的错误:
int? i = null;
i.Value.ToString()
为了检查你的int?具有您可以访问的值 i.HasValue
原因很简单。 int?
或 Nullable<int>
是 struct 或 value type,它永远不能为 null。
那么当我们这样做时会发生什么:
int? _cost = null;
_cost
将有两个字段 Value
和 HasValue
,当我们将 null
分配给 _cost
时,它的 HasValue
标志将设置为 false
,并且 Value
字段将分配为 default(T)
int?
的情况下会是 0
。
现在,当我们在 _cost
上调用 ToString
时,Nullable<T>
有一个 ToString
的覆盖定义,如果我们查看 Microsoft's provided Source Reference,它的实现如下:
public override string ToString() {
return HasValue ? value.ToString() : "";
}
因此它返回一个空字符串,因为 _cost
被分配了 null
。
现在是 string _serialNumber
的情况。作为 string
,它是一个引用类型,它可以纯粹持有 null
。如果它持有 null
,则对其调用 ToString
将按预期产生空引用异常。
您可能会看到:Value Types and Reference Types - MSDN
我认为原因是,当编译器遇到原始数据类型时,它会将其包装到相应的对象中。 toString() 方法调用在这里只是间接调用(包装然后调用方法),异常在那里处理。而在 String 的情况下,我们直接调用该方法。当指向 null 时,该方法会引发异常。
TextBox2.Text = selectedItem.SerialNumber.ToString(); //error
产生错误,因为它正在调用 System.String 的成员函数 ToString()。此函数返回此 System.String 实例;不执行实际转换。此外,String 是一种引用类型。引用类型包含指向另一个保存数据的内存位置的指针。
TextBox1.Text = selectedItem.Cost.ToString(); //no error
不会产生错误,因为它调用的是 System.Integer 的成员函数 ToString()。此函数将此实例的数值转换为其等效的字符串表示形式。此外,整数是一种值类型。如果数据类型将数据保存在自己的内存分配中,则该数据类型是值类型。
相同的函数名称 ToString() 但执行不同的任务。
Value types and reference types
Nullable<T>
类型的实现。int?
或Int32?
。不是Int?
Cost
为int?
类型的 null,selectedItem.Cost.GetType()
将 抛出。不同之处在于ToString
是一个被Nullable<T>
覆盖的虚方法,因此可以直接在int?
上调用。另一方面,GetType
是在Nullable<T>
的基类上声明的非虚拟方法(实际上是在object
上)。因此,运行时必须先将Nullable<>
转换为该类类型,然后才能调用GetType
,这就是所谓的 boxing,但Nullable<>
具有特殊的装箱规则,因此为 null产生参考。