为啥空字符串上的 .ToString() 会导致空错误,而 .ToString() 在具有空值的可空 int 上工作正常?

Posted

技术标签:

【中文标题】为啥空字符串上的 .ToString() 会导致空错误,而 .ToString() 在具有空值的可空 int 上工作正常?【英文标题】:Why does .ToString() on a null string cause a null error, when .ToString() works fine on a nullable int with null value?为什么空字符串上的 .ToString() 会导致空错误,而 .ToString() 在具有空值的可空 int 上工作正常? 【发布时间】:2012-07-11 21:48:02 【问题描述】:

selectedItem 有两个字段:

int? _cost string _serialNumber

在此示例中,selectedItem_cost_serialNumber 都为空。我正在通过它们的属性阅读selectedItem 的字段,并用它们的值填充文本框,当...

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

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

可空对象必须有一个值。

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

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

【问题讨论】:

您链接的问题根本不一样。这个问题是关于 MessageBox.ShowString.Concat 如何与 null 字符串一起工作。 请注意,您使用“可空”这个词来表示两个完全不同的东西。 int? 是一个值类型,称为Nullable<T>,对空值有特殊处理; string 是一个引用类型(虽然有点奇怪),它实际上有一个值nullint? 总是有一个价值,它只是有一种特殊的方式来表达“我现在表现得像 null。” 【参考方案1】:

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

查看msdn 了解可空类型。

【讨论】:

所以基本上是因为它实际上不是null,它只是假装是? 是的,基本上它是一个结构,它包裹着一个 int 字段和一个布尔符号,指示这个结构是设置为 int 值,还是仍然未初始化,这意味着 null。在this 主题中,Mark Gravell 将实现粘贴在Nullable<T> 类型上。 int?Int32?。不是Int? 这一切都是正确的,但还要注意selectedItem.Cost.GetType() 抛出如果Costint? 类型的空值。不同之处在于ToString 是一个被Nullable<T> 覆盖的虚方法,因此可以直接在int? 上调用。另一方面,GetType 是在Nullable<T> 的基类上声明的非虚拟方法(实际上是在object 上)。因此,运行时必须先将Nullable<> 转换为该类类型,然后才能调用GetType,这就是所谓的装箱,但Nullable<> 有特殊的装箱规则,因此为空参考产生。【参考方案2】:

Nullable<int>struct,不能真正为空。所以对“null”结构的方法调用仍然有效。

有一些“编译器魔法”使_cost == null 成为一个有效的表达式。

【讨论】:

这是编译器的魔法。使用普通 C# 代码(即运算符重载)添加从 nullNullable<T> 的隐式转换将要求您能够从表达式“null”中推断出一个类型,而您不能,因为它没有没有。编译器本质上将Nullable<T> foo = null 转换为Nullable<T> foo = default(Nullable<T>)foo == null 转换为!foo.HasValue【参考方案3】:

int? 本身并不是一个对象,而是一个Nullable<int> 对象。

所以当你声明 int? _Cost 时,你实际上是在声明 Nullable<int> _Cost 并且 _Cost.Value 的属性是 undefined 而不是 _Cost 对象本身。

它实际上是一种语法糖,可以轻松使用intbooldecimalnon nullable 类型。

根据MSDN:

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

【讨论】:

从技术上讲,当_Cost 为空时(即_Cost.HasValue 为假),_Cost.Value 未定义 @AlexBrault: _Cost 不可能是 null,因为它不是引用类型。 @comecme:括号内的说明。【参考方案4】:

字符串是引用类型,但可为空的 int 是值类型。这是对 http://www.albahari.com/valuevsreftypes.aspx 差异的一个很好的讨论。

【讨论】:

【参考方案5】:

Nullable 实际上是一个暴露两个属性的结构:HasValue 和 Value。如果你这样做,你会得到你的错误:

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

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

【讨论】:

完全正确。该代码不会抛出NullReferenceException,而是InvalidOperationException。见Nullable(T).Value。【参考方案6】:

我认为原因是,当编译器遇到原始数据类型时,它会将其包装到相应的对象中。 toString() 方法调用在这里只是间接调用(包装然后调用方法),异常在那里处理。 而在 String 的情况下,我们直接调用该方法。当指向 null 时,该方法抛出异常。

【讨论】:

【参考方案7】:

原因很简单。 int?Nullable<int>struct 或 value type,它永远不能为空

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

int? _cost = null;

_cost 将有两个字段ValueHasValue,当我们将null 分配给_cost 时,它的HasValue 标志将被设置为false 并且Value 字段将被分配@如果是int?,则为987654336@,它将为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

【讨论】:

【参考方案8】:
TextBox2.Text = selectedItem.SerialNumber.ToString(); //error

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

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

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

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

String.ToString Method

Int32.ToString Method

Value types and reference types

【讨论】:

以上是关于为啥空字符串上的 .ToString() 会导致空错误,而 .ToString() 在具有空值的可空 int 上工作正常?的主要内容,如果未能解决你的问题,请参考以下文章

为啥替换在空字符串上不返回任何内容

为啥在此 JSON 字符串上使用 json_decode 会导致空值

为啥隐式符号到字符串的转换会导致 JavaScript 中的 TypeError?

为啥我的字符串分配会导致分段错误?

为啥在构造函数中抛出异常会导致空引用?

为啥这个不允许编译器执行的示例会导致使用 cmov 取消引用空指针?