可为null的布尔值上的GetType

本文关键字:GetType 布尔值 可为 null | 更新日期: 2023-09-27 17:58:20

当我在MicrosoftMSDN 上发现这篇文章时,我正在研究可为null的布尔

如何:识别可为null的类型(C#编程指南)

您可以使用C#typeof运算符来创建一个表示可为null类型的Type对象。

所以我试着用一个可以为null的bool:进行检查

Console.Write(typeof(bool?)); //System.Nullable`1[System.Boolean]

MSDN上的文章说

您还可以使用System.Reflection命名空间的类和方法来生成表示可为null类型的Type对象。但是,如果您试图在运行时使用GetType方法或is运算符从Nullable变量中获取类型信息,则结果是表示基础类型的type对象,而不是Nullable类型本身。

对可为null的类型调用GetType会导致在该类型隐式转换为Object时执行装箱操作因此,GetType总是返回一个表示基础类型的Type对象,而不是Nullable类型

如果这是真的,无论我使用可为null的布尔还是常规布尔,我都希望从.GetType()中得到相同的结果。但事实并非如此:

    bool a = new bool();
    Console.Write(a.GetType()); //Prints System.Boolean
    bool? b = new bool?();
    Console.Write(b.GetType()); //Exception!

发生的异常:

BoolTest.exe 中发生类型为"System.NullReferenceException"的未处理异常

附加信息:对象引用未设置为对象的实例。

但是对象引用被设置为对象的实例。这个错误的原因可能是什么?

可为null的布尔值上的GetType

您的实际问题似乎是:

通过使用默认构造函数(例如bool? b = new bool?();)将Nullable<T>初始化为默认值,为什么访问b的成员(如GetType())会抛出NullReferenceExceptionnew不是总是返回一个值,所以b不可能真的是null吗?

嗯,是和否。Nullable<T>有点特别。来自C#规范:

4.1.10可为空的类型

[…]

可为null类型T的实例?具有两个公共只读属性:

  • 布尔类型的HasValue属性
  • T类型的Value属性

HasValue为true的实例称为非null。非null实例包含一个已知值,value返回该值。HasValue为false的实例称为null。null实例具有未定义的值。尝试读取null实例的值会引发System.InvalidOperationException。

所以,是的,bool? b = new bool?();确实返回了一个实例:一个你只能调用HasValue的实例。由于它返回false,你不能对该实例做太多其他事情。

然后是下一个相关部分:

4.3.1装箱转换

如果可为null类型的值是null值(HasValue为false),则装箱会生成null引用

这在MSDN中也有解释:Boxing Nullable Types(C#编程指南):

只有当对象为非null时,才会对基于可为null类型的对象进行装箱。如果HasValue为false,对象引用将被指定为null,而不是装箱

进一步了解规格:

11.3.5装箱和拆箱

当结构类型重写从System.Object继承的虚拟方法(如Equals、GetHashCode或ToString)时,通过结构类型的实例调用虚拟方法不会导致装箱。

GetType()未被Nullable<T>覆盖,因此将发生装箱。当您在结构上调用GetType()或任何非重写方法时,在调用该方法之前,该结构将被装箱到对象中。在Nullable<T>为空的情况下,装箱操作的结果将是(object)null。因此出现了例外。

另请参阅在.NET中对值类型调用方法会导致装箱吗?。


因此,为了回答您的问题:

  • b而不是null,它持有Nullable<bool>,其中HasValue指示false
  • 对其调用未重写的GetType()方法会导致Nullable<bool>结构被装箱,以便访问底层方法object.GetType()
  • 这个装箱操作实际上并不装箱,只是简单地返回(object)null
  • 最后,您调用((object)null).GetType(),它抛出您遇到的NullReferenceException

如果你真的在寻找一段可以返回任何变量类型的代码,即使是空的Nullable<T>,也可以使用这样的代码:

Type GetType<T>(T obj) 
{ 
    return typeof(T); 
}

你可以这样称呼它:

Console.WriteLine(GetType(b));

您在NULL引用上调用GetType(装箱不带值的Nullable Type的结果)。

bool? b = new bool?();相当于bool? b = null;

尝试此操作以获得正确的结果:

bool? b = new bool?(false);
Console.Write(b.GetType()); // System.Boolean

该文档意味着,如果在具有值(Not Null)的Nullable对象上成功调用GetType()。您得到的基本类型是System.Boolean。但是不能使用NULL引用调用任何方法,这是适用于任何引用类型的一般规则。

要清除= nullnew bool?()之间的等价点,请检查此Fiddle。两者都生成相同的IL:

IL_0001:  ldloca.s   V_0
IL_0003:  initobj    valuetype [mscorlib]System.Nullable`1<bool>

IL_0009:  ldloca.s   V_1
IL_000b:  initobj    valuetype [mscorlib]System.Nullable`1<bool>