当.ToString()在具有null值的可为null的int上正常工作时,为什么对null字符串的.ToString()会导致null错误?


70

selectedItem 有两个字段:

  • int? _cost
  • string _serialNumber

在这个例子中,_cost_serialNumberselectedItem都是空。我正在阅读通过selectedItem其属性的字段,并在...时在文本框中填充其值。

TextBox1.Text = selectedItem.Cost.ToString(); //no error
TextBox2.Text = selectedItem.SerialNumber.ToString(); //error

我知道这SerialNumber.ToString()是多余的(因为它已经是一个字符串),但是我不明白为什么这会导致此异常:

可为空的对象必须具有一个值。

  • int? _cost 可以为空,并且没有值,但是它没有给我例外。
  • string _serialNumber可以为空,并且没有值,但是确实给了我例外。

这个问题触及了这个问题,这个家伙本质上是在问同样的事情,但是没有指定的答案,也没有解释为什么可以为空int?例如,我可以.ToString()在可为null的int上使用,但不能在null字符串上使用吗?


您链接到的问题根本不一样。这个问题是关于如何MessageBox.Show和如何String.Concat使用null字符串。
Mark Byers

9
请注意,您使用的是“ nullable”一词,它表示两个完全不同的事物。int?是一个值类型,称为Nullable<T>,对空值有特殊处理;string是一种参考类型(虽然有些奇怪),但实际上可以具有的值null。一个int?始终具有价值,它只是用一种特殊的方式来表达“我现在的行为举止” null
Michael Edenfield

Answers:


90

因为string类型null的确指向任何东西,所以内存中没有任何对象。
但是,int?即使类型(可空)的值设置为null仍然指向某个对象。
如果阅读Jeffrey Richter的“通过C#进行CLR”,您会发现可空类型只是具有某些封装逻辑的通用类型的外观类,从而使使用DB空更方便。

检查msdn以了解可为空的类型。


8
所以基本上是因为它实际上不是null,而是假装为null?
The Oddler

3
是的,基本上,这是一个包裹在int字段和布尔符号周围的结构,指示该结构是设置为int值还是仍是统一的(表示null)。在主题中,Mark Gravell在Nullable<T>类型上粘贴了实现。
Johnny_D

1
int?Int32?。不是Int?
turbanoff 2012年

3
这都是正确的,但还要注意,如果type为null ,selectedItem.Cost.GetType() 则会抛出该异常。区别在于,这是一个虚拟方法,该方法被覆盖,因此可以直接调用。另一方面,是在(实际上是on)的基类上声明的非虚拟方法。因此,运行时必须先将转换为该类类型,然后才能调用,这就是装箱Costint?ToStringNullable<T>int?GetTypeNullable<T>objectNullable<>GetType,但是Nullable<>具有特殊的装箱规则,因此会生成空引用。
Jeppe Stig Nielsen

谢谢。我最困惑的是为什么null时MyNullableBool.ToString返回string.Empty并不抛出MyNullableBool
ProfK '18

33

ANullable<int>是一个struct,不能真正为null。因此,对“空”结构的方法调用仍然有效。

有一些“编译器魔术”可以使_cost == null表达式有效。


4
这是编译器的魔力。使用普通C#代码添加从null到的隐式转换Nullable<T>(即,运算符重载)将要求您能够从表达式“ null”中推断出一个类型,但您不能这样做,因为它没有一个。编译器实质上是将Nullable<T> foo = null进入Nullable<T> foo = default(Nullable<T>)foo == null进入!foo.HasValue
亚当·罗宾逊

14

int?实际上并不是一个对象,而是一个Nullable<int>对象。

所以,当你宣布int? _Cost,你实际上是宣布Nullable<int> _Cost和财产_Cost.Valueundefined不是_Cost对象本身。

它实际上是一个语法糖使用non nullable类型,如intbooldecimal容易。

根据MSDN

语法T?是的简写System.Nullable<T>,其中T是值类型。两种形式可以互换。


从技术上讲,_Cost.Value_Costnull时(即_Cost.HasValuefalse时)是不确定的
3Doubloons

3
@AlexBrault:_Cost不能真的,null因为它不是引用类型。
comecme 2012年

@comecme:因此在括号中进行了澄清。
3Doubloons,2012年


2

Nullable实际上是一个公开两个属性的结构:HasValue和Value。如果这样做,您将得到错误:

int? i = null;
i.Value.ToString()

为了检查你的int?具有您可以访问的值i.HasValue


2
完全正确。该代码不会抛出NullReferenceException,而是抛出InvalidOperationException。请参见Nullable(T).Value
comecme 2012年

1

我认为原因是,当编译器遇到原始数据类型时,将其包装到其对应的对象。toString()方法调用只是一个间接调用(将其包装并随后调用该方法),并在此处处理异常。对于String,我们直接调用该方法。当指向null时,该方法将引发异常。


1

原因很简单。int?Nullable<int>structvalue类型则永远不能为null

那么当我们这样做时会发生什么:

int? _cost = null;

_cost将有两个字段ValueHasValue,当我们分配null_cost它的 HasValue标志将被设置为falseValue字段将被分配default(T)在的情况下,int?它会0

现在,当我们调用ToString_costNullable<T>有一个的覆盖定义ToString,如果我们看一下微软提供的Source Reference,它的实现如下:

public override string ToString() {
    return HasValue ? value.ToString() : "";
}

由于_cost分配了,因此它返回一个空字符串null

现在是的情况string _serialNumber。作为string它是一个引用类型,它可以完全保留null。如果保留,null则对其进行调用ToString将产生预期的Null Reference Exception。

您可能会看到:值类型和引用类型-MSDN


0
TextBox2.Text = selectedItem.SerialNumber.ToString(); //error

产生错误是因为它正在调用函数ToString(),该函数是System.String的成员。此函数返回此System.String实例;没有执行任何实际的转换。另外,String是引用类型。引用类型包含指向另一个保存数据的存储位置的指针。

TextBox1.Text = selectedItem.Cost.ToString(); //no error

不会产生错误,因为它正在调用函数ToString(),该函数 是System.Integer的成员。此函数将该实例的数值转换为其等效的字符串表示形式。另外,Integer是一个值类型。如果数据类型将数据保存在其自己的内存分配中,则它是一种值类型。

相同的函数名称ToString()但执行不同的任务。

String.ToString方法

Int32.ToString方法

值类型和引用类型

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.