int? 竟然真的可以是 null!.NET/C# 确定可空值类型 Nullable<T> 实例的真实类型

栏目: ASP.NET · 发布时间: 5年前

内容简介:使用等等!除了本文提到的一些情况。注意看以下的代码。我们创建了一个值为

使用 Nullable<T> 我们可以为原本不可能为 null 的值类型像引用类型那样提供一个 null 值。不过注意: Nullable<T> 本身也是个 struct ,是个值类型哦。这意味着你随时可以调用 .HasValue 这样的方法,而不用担心会出现 NullReferenceException

等等!除了本文提到的一些情况。

Nullable 中的 null

注意看以下的代码。我们创建了一个值为 nullint? ,然后依次输出 value 的值、 value.GetType()

你觉得可以得到什么结果呢?

public class Program
{
    public static void Main(string[] args)
    {
        int? value = GetValue(null);

        Console.WriteLine($"value = {value}");
        Console.WriteLine($"type  = {value.GetType()}");
        Console.WriteLine($"TYPE  = {typeof(int?)}");

        Console.ReadLine();
    }

    private static int? GetValue(int? source) => source;
}

结果是……

果是……

是……

……

崩掉了……

int? 竟然真的可以是 null!.NET/C# 确定可空值类型 Nullable<T> 实例的真实类型

那么我们在 value 后面加个空传递运算符:

--  Console.WriteLine($"type  = {value.GetType()}");
++  Console.WriteLine($"type  = {value?.GetType()}");

现在再次运行,我们确认了 value?.GetType() 的值为 null ;而 typeof(int?) 的类型为 Nullable<Int32>

int? 竟然真的可以是 null!.NET/C# 确定可空值类型 Nullable<T> 实例的真实类型

然而,我们现在将 value 的值从 null 改为 1

--  int? value = GetValue(null);
++  int? value = GetValue(1);

竟然 value.GetType() 得到的类型是 Int32

int? 竟然真的可以是 null!.NET/C# 确定可空值类型 Nullable<T> 实例的真实类型

于是我们可以得出结论:

  1. 对于可空值类型,当为 null 时, GetType() 会出现空引用异常;
  2. 对于可空值类型,当不为 null 时, GetType() 返回的是对应的基础类型,而不是可空值类型;
  3. typeof(int?) 能够得到可空值类型。

Object.GetType() 和 is 对 Nullable 的作用

docs.microsoft.com 中,有一段对此的描述:

When you call the Object.GetType method on an instance of a nullable type, the instance is boxed to Object . As boxing of a non-null instance of a nullable type is equivalent to boxing of a value of the underlying type, GetType returns a Type object that represents the underlying type of a nullable type.

意思是说,当你对一个可空值类型 Nullable<T> 调用 Object.GetType() 方法的时候,这个实例会被装箱,会被隐式转换为一个 object 对象。然而对可空值类型的装箱与对值类型本身的装箱是同样的操作,所以调用 GetType() 的时候都是返回这个对象对应的实际基础类型。例如对一个 int? 进行装箱和对 int 装箱得到的 object 对象是一样的,于是 GetType() 实际上是不能区分这两种情况的。

那什么样的装箱会使得两个不同的类型被装箱为同一个了呢?

另一篇文档 描述了 Nullable<T> 装箱的过程:

  • If HasValue returns false, the null reference is produced.
  • If HasValue returns true, a value of the underlying value type T is boxed, not the instance of Nullable .
  • 如果 HasValue 返回 false ,那么就装箱一个 null
  • 如果 HasValue 返回 true ,那么就将 Nullable<T> 中的 T 进行装箱,而不是 Nullable<T> 的实例。

这才是为什么 GetType() 会得到以上结果的原因。

同样的,也不能使用 is 运算符来确定这个类型到底是不是可空值类型:

Console.WriteLine($"value is int  = {value is int}");
Console.WriteLine($"value is int? = {value is int?}");

最终得到两者都是 True

int? 竟然真的可以是 null!.NET/C# 确定可空值类型 Nullable<T> 实例的真实类型

应该如何判断可空值类型的真实类型

使用 Nullable.GetUnderlyingType(type) 方法,能够得到一个可空值类型中的基础类型,也就是得到 Nullable<T>T 的类型。如果得不到就返回 null

所以使用以下方法可以判断 type 的真实类型。

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

然而,这个 type 的实例怎么来呢?根据前面的示例代码,我们又不能调用 GetType() 方法。

实际上,这个 type 的实例就是拿不到,在运行时是不能确定的。我们只能在编译时确定,就像下面这样:

bool IsOfNullableType<T>(T _) => Nullable.GetUnderlyingType(typeof(T)) != null;

如果你是运行时拿到的可空值类型的实例,那么实际上此方法也是无能为力的。

public class Program
{
    public static void Main(string[] args)
    {
        Console.Title = "walterlv's demo";

        int? value = GetValue(1);
        object o = value;
        Console.WriteLine($"value is nullable? {IsOfNullableType(value)}");
        Console.WriteLine($"o     is nullable? {IsOfNullableType(o)}");

        Console.ReadLine();
    }

    private static int? GetValue(int? source) => source;

    static bool IsOfNullableType<T>(T _) => Nullable.GetUnderlyingType(typeof(T)) != null;
}

int? 竟然真的可以是 null!.NET/C# 确定可空值类型 Nullable<T> 实例的真实类型

参考资料


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

自媒体之道

自媒体之道

吴晨光 / 中国人民大学出版社有限公司 / 2018-7-31 / CNY 49.80

内容简介: 《自媒体之道》为你解决四个问题: 1. 一篇10万+的刷屏文章是怎么写出来的? 2. 一支10万+的爆款视频是怎么做出来的? 3. 一个100万+粉丝的大号是怎么运营的? 4. 神秘的人工智能(AI)和备受争议的算法是怎么运行的? 这部超过30万字的著作,集纳了中国顶尖自媒体人和平台运营者3年的实操经验。全书13章,详解案例150+,提炼“锦囊秒句”......一起来看看 《自媒体之道》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换