有人可以帮助解释为什么在空引用类型上调用ToString()
会导致异常(在我看来很有道理,你无法调用任何方法!)但是调用{{1}在空ToString()
上返回Nullable(Of T)
?这对我来说是一个惊喜,因为我认为这种行为在各种类型中都是一致的。
String.Empty
答案 0 :(得分:20)
Nullable<T>
实际上是struct
,它具有一些编译器支持和实现支持,其行为类似于null
而实际上不是null
。
您所看到的是实现之间的冲突,允许您将其自然地视为null
,就像您使用任何其他引用类型一样,但允许方法调用发生,因为Nullable<T>
isn' t实际上为null,其中的值为null。
视觉上它看起来应该不起作用,这只是因为你不能看到在后台为你完成的工作。
当您在空引用类型上调用扩展方法时,可以看到其他此类视觉欺骗...调用有效(反对视觉期望),因为在引擎盖下,它被解析为静态方法调用,将您的null实例作为参数。
答案 1 :(得分:6)
Nullable是一种值类型,null
的分配使其Value=null
和HasValue=false
初始化。
此外,Nullable.ToString()的实现如下:
public override string ToString()
{
if (!this.HasValue)
{
return "";
}
return this.value.ToString();
}
所以你所看到的是预期的。
答案 2 :(得分:4)
可空类型有点棘手。当您将其设置为null
时,它实际上不是null
,因为它不是引用类型(它是值类型)。当您使用null
初始化此类变量时,它会创建新的sctructure实例,其中HasValue
属性为false
且Value
为null
,因此当您调用{{1}时它在结构实例上运行良好。
答案 3 :(得分:1)
调用default(object).ToString()
引发的异常被称为NullReferenceException
,原因是它在空引用上调用方法。另一方面,default(int?)
不是空引用,因为它不是引用;它是一个值类型,其值等于为null。
最重要的一点是,如果这样做了,那么下面就会失败:
default(int?).HasValue // should return false, or throw an exception?
这也会搞砸我们混淆nullables和non nullables的能力:
((int?)null).Equals(1) // should return false, or throw an exception?
以下内容变得毫无用处:
default(int?).GetValueOrDefault(-1);
我们可以摆脱HasValue
并强制与null进行比较,但是如果在某些情况下与null相比,可变为null的value-type的相等覆盖可以返回true。这可能不是一个好主意,但它可以完成,语言必须应对。
让我们回想一下为什么可以引入可空类型。引用类型可以为null的可能性是引用类型概念中固有的,除非努力强制执行非可空性:引用类型是引用某些东西的类型,这意味着可能没有引用任何东西,我们称之为 null 。
虽然在许多情况下令人讨厌,但我们可以在各种情况下使用它,例如表示“未知值”,“无效值”等等(我们可以将它用于 null < / em>表示在数据库中,例如)。
此时,我们在给定的上下文中给出了 null 一个含义,超出了给定引用不引用任何对象的简单事实。
由于这很有用,因此我们可能希望将int
或DateTime
设置为null,但我们不能,因为它们不是引用其他内容的类型,因此可以'因为哺乳动物会失去我的羽毛,所以我处于一种不提及任何东西的状态。
2.0引入的可空类型通过与引用类型不同的机制为我们提供了一种可以具有语义 null 的值类型。大多数情况下你可以编码自己,如果它不存在,但特殊的拳击和促销规则允许更明智的拳击和操作员使用。
好。现在让我们考虑为什么NullReferenceExceptions
首先发生。其中两个是不可避免的,一个是C#中的设计决策(并不适用于所有.NET)。
callvirt
而不是call
来强制NullReferenceException
以保持一致性(不能说我同意,但你去了。)。这些案例都不以任何方式适用于可以为空的值类型。将可空值类型置于无法知道哪个字段或方法覆盖访问的条件是不可能的。 NullReferenceException
的整个概念在这里没有意义。
总而言之,不要抛出NullReferenceException
与其他类型一致 - 当且仅当使用空引用时才通过它。
请注意,有一种情况是调用null nullable-type throws,它使用GetType()
,因为GetType()
不是虚拟的,并且当在值类型上调用时总会有隐含的拳击。对于其他值类型也是如此:
(1).GetType()
被视为:
((object)1).GetType()
但是在可以为空的类型的情况下,装箱将带有假HasValue
的那些变为空,因此:
default(int?).GetType()
被视为:
((object)default(int?)).GetType()
导致GetType()
在空对象上被调用,因此抛出。
顺便提一下,为什么不伪造NullReferenceType
是更明智的设计决策 - 那些需要这种行为的人总是可以装箱。如果您希望它通过,那么使用((object)myNullableValue).GetString()
,因此不需要该语言将其视为强制异常的特殊情况。
修改强>
哦,我忘了提及NullReferenceException
背后的机制。
NullReferenceException
的测试非常便宜,因为它主要是忽略了问题,然后在操作系统中捕获异常(如果发生)。换句话说,没有测试。
请参阅What is the CLR implementation behind raising/generating a null reference exception?,并注意其中任何一种都不适用于可空值类型。
答案 4 :(得分:0)
如果您调查Nullable<>
定义,则会覆盖ToString定义。在此函数中,ToString被覆盖以返回String.Empty。
// Summary:
// Returns the text representation of the value of the current System.Nullable<T>
// object.
//
// Returns:
// The text representation of the value of the current System.Nullable<T> object
// if the System.Nullable<T>.HasValue property is true, or an empty string ("")
// if the System.Nullable<T>.HasValue property is false.
public override string ToString();
另一方面,Stock是一个自定义类,我假设ToString没有被覆盖。因此它返回NullReferenceException,因为它使用默认行为。
答案 5 :(得分:0)
根据MSDN备注
Guid.ToSTring()方法返回的字符串表示形式 根据提供的格式,此Guid实例的值 说明符。
根据Nullable
上的MSDN备注如果可以为类型分配值,则可以将类型视为可为空 赋值为null,表示该类型没有任何值。 因此,可空类型可以表示值,或者没有值 存在。例如,String等引用类型可以为空, 而诸如Int32之类的值类型则不是。值类型不能 可以为空,因为它有足够的容量来表达唯一的值 适合该类型;它没有额外的容量 需要表示null值。