当前位置: 首页 > news >正文

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

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

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


本文内容

      • Nullable 中的 null
      • Object.GetType() 和 is 对 Nullable 的作用
      • 应该如何判断可空值类型的真实类型
        • 参考资料

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;
}

结果是……


果是……


是……


……



崩掉了……

NullReferenceException

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

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

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

null 的类型

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

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

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

1 的类型

于是我们可以得出结论:

  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

用 is 确定类型

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

使用 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;
}

运行时是拿不到的


参考资料

  • c# - Nullable type is not a nullable type? - Stack Overflow
  • How to: Identify a nullable type - C# Programming Guide - Microsoft Docs
  • Using nullable types - C# Programming Guide - Microsoft Docs

我的博客会首发于 https://walterlv.com/,而 CSDN 和博客园仅从其中摘选发布,而且一旦发布了就不再更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://blog.csdn.net/wpwalter),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • Slack 开发入门之 Incoming Webhooks:往 Slack 的 Channel 中发消息
  • 三值 bool? 进行与或运算后的结果
  • 为什么我们不应该使用微信或者 QQ 作为团队协作的 IM 工具?
  • 通过重写预定义的 Target 来扩展 MSBuild / Visual Studio 的编译过程
  • .NET 中 GetHashCode 的哈希值有多大概率会相同(哈希碰撞)
  • C# 中委托实例的命名规则
  • 在 Target 中获取项目引用的所有依赖(dll/NuGet/Project)的路径
  • 让 MSBuild Target 支持 Clean
  • C#/.NET 如何确认一个路径是否是合法的文件路径
  • 不使用 U 盘等任何工具全新安装 Windows 操作系统
  • C# 永远不会返回的方法真的不会返回
  • CentOS 的终端中如何搜索文件
  • 如何在命令行中监听用户输入文本的改变?
  • 使用 Xamarin 开发 iOS 键盘扩展(含网络访问)
  • 使用 Xamarin 开发 iOS 应用中需要注意的若干个问题
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • 0x05 Python数据分析,Anaconda八斩刀
  • Android 架构优化~MVP 架构改造
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • go append函数以及写入
  • input实现文字超出省略号功能
  • Java方法详解
  • linux安装openssl、swoole等扩展的具体步骤
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • MyEclipse 8.0 GA 搭建 Struts2 + Spring2 + Hibernate3 (测试)
  • React16时代,该用什么姿势写 React ?
  • React-生命周期杂记
  • Spring声明式事务管理之一:五大属性分析
  • 驱动程序原理
  • 如何胜任知名企业的商业数据分析师?
  • 什么软件可以剪辑音乐?
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 我的面试准备过程--容器(更新中)
  • 用jquery写贪吃蛇
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • # C++之functional库用法整理
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • #git 撤消对文件的更改
  • #基础#使用Jupyter进行Notebook的转换 .ipynb文件导出为.md文件
  • (007)XHTML文档之标题——h1~h6
  • (26)4.7 字符函数和字符串函数
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (七)Java对象在Hibernate持久化层的状态
  • (十三)Maven插件解析运行机制
  • (转载)深入super,看Python如何解决钻石继承难题
  • (轉貼) 資訊相關科系畢業的學生,未來會是什麼樣子?(Misc)
  • ./configure、make、make install 命令
  • .gitignore文件设置了忽略但不生效
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .NET6 开发一个检查某些状态持续多长时间的类