• C#可空类型



    一、为什么需要可空类型

    1. 我们都知道数据库里的数值型字段都是可空的,比如某个Int字段,它可以有值也可以没有值,我们要把这个字段读取出来保存在一个Int类型变量里,就必须考虑到它没有值的情形。这个时候,就需要借助可空类型来实现了。
    2. 下面是一个例子: Java 的 java.util.Date 类是引用类型,所以该类型的变量能设为null,但是CLR的System.DateTime 定值类型,DateTime 变量永远不能设为 null 。如果用 Java 写的应用程序想和运行 CLR 的 Web 服务交流日期/时间,那么一旦 Jaya 程序发送 null酒会出问题,因为 CLR 不知道如何表示 nul ,也不知道如何操作它。
    3. 经常出现有时候我们开发的时候,希望没有初始化一个值与初始化作区分,之前的做法是约定一个很特别的值,然后判断是不是相等来决定要不要赋值。

    二、System.Nullable< T >源码

    先展示一下源码:
    源码地址:https://referencesource.microsoft.com/#mscorlib/system/nullable.cs,ffebe438fd9cbf0e

    public struct Nullable<T> where T : struct
    {
        private bool hasValue; 
        internal T value;
    
        [System.Runtime.Versioning.NonVersionable]
        public Nullable(T value) {
            this.value = value;
            this.hasValue = true;
        }        
    
        public bool HasValue {
            [System.Runtime.Versioning.NonVersionable]
            get {
                return hasValue;
                }
            } 
    
        public T Value {
            get {
                if (!hasValue) {
                    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue);
                }
                return value;
            }
        }
    
        [System.Runtime.Versioning.NonVersionable]
        public T GetValueOrDefault() {
            return value;
        }
    
        [System.Runtime.Versioning.NonVersionable]
        public T GetValueOrDefault(T defaultValue) {
            return hasValue ? value : defaultValue;
        }
    
        public override bool Equals(object other) {
            if (!hasValue) return other == null;
            if (other == null) return false;
            return value.Equals(other);
        }
    
        public override int GetHashCode() {
            return hasValue ? value.GetHashCode() : 0;
        }
    
        public override string ToString() {
            return hasValue ? value.ToString() : "";
        }
    
        [System.Runtime.Versioning.NonVersionable]
        public static implicit operator Nullable<T>(T value) {
            return new Nullable<T>(value);
        }
    
        [System.Runtime.Versioning.NonVersionable]
        public static explicit operator T(Nullable<T> value) {
            return value.Value;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    根据上面代码,可以看到 Nullable模板也是一个结构体,值类型,也是轻量级的。只不过增加了一个bool hasValue,这个值在构造结构体的时候设置为true。之后如果获取value的话,如果没构造,就会抛出异常。
    因此,在代码中构造一个可空的int,可以写:

    using System;
    
    namespace ConsoleApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                Nullable<int> x = 5;
                Nullable<int> y = null;
    
                Console.WriteLine($"x hasvalue : {x.HasValue} Value: {x.Value}");
                Console.WriteLine($"y hasvalue : {y.HasValue} Value: {y.GetValueOrDefault()}");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    结果如下:
    在这里插入图片描述

    三、c#对可空类型的支持

    虽然声明一个Nullable模板可以做到可空类型,但是官方还是觉得写起来麻烦,所以c# 2.0以后就在语言层面添加了对可空类型的支持。
    C#允许使用?表示法来声明:

    int? x1 = 5;
    int? y1 = null;
    
    • 1
    • 2

    在c#中 int? 就等于 Nullable< T >

    允许向可空实例应用操作符:

    using System;
    
    namespace ConsoleApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                int? x = 5;
                int? y = null;
                //一元
                x++;
                y = -y;
                //二元
                x += 3;
                y += 3;
                //相等性
                if (x == y)
                {
    
                }
                //比较
                if (x < y)
                {
    
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    所有运算中,只要一个值为null 那么结果就是null

    三、c#的空接合操作符

    c#土工了一个“空接合操作符( null - coalescing operator ),即??操作符,它要获取两个操作数。假如左边的操作数不为 nul ,就返回这个操作数的值。如果左边的操作数为 null ,就返回右边的操作数的值。利用空接合操作符,可以方便地设置变量的默认值。
    空接操作符的一个好处在于,它既能用于引用类型,也能用于可空值类型。以下代码演示了如何使用??操作符:

    using System;
    namespace ConsoleApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                int? x = null;
                int? y = x.HasValue ? x : 123;
                int? z = x ?? 123;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    上面代码中,z的初始化,等价于y的初始化,但是更简便。

    四、可空类型的装箱拆箱:

    using System;
    namespace ConsoleApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                int? x = null;
                object o = x;
                Console.WriteLine($"o is null ? {o == null}");
    
                x = 5;
                o = x;
                Console.WriteLine($"o is null ? {o == null}");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述
    从上面结果可以看到装箱的时候需要看可空类型是不是为null,如果为空不会发生装箱操作,object仍让是null。

    using System;
    namespace ConsoleApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                object o = 5;
                int? x = (int?)o;   // 5
                int y = (int)o;     // 5
                Console.WriteLine($"x: {x.Value} y {y}");
    
                o = null;
                int? x1 = (int?)o;  // null
                int y1 = (int)o;    //System.NullReferenceException:“Object reference not set to an instance of an object.”
    
                Console.WriteLine($"x1: {x1.Value} y1 {y1}");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    从上面代码可以看出来,拆箱对于非null对象可以转成 int 或者 int?,但是对于null对象,如果转成int则会抛出异常。

    五、可空类型的GetType()

    在Nullable< T >对象上调用GetType()方法,CLR实际上会撒谎说类型是T,而不是Nullable< T >。
    因此运行如下代码:

    int? x = 5;
    Console.WriteLine($"x.GetType() : {x.GetType()}");
    
    • 1
    • 2

    结果:
    在这里插入图片描述

    总结

    可空类型,一方面兼容了一些业务对null值类型的需求,一方面提高了代码的简练性。因此其实可以在写代码的时候适当的习惯使用。

  • 相关阅读:
    android NetworkMonitor和ConnectivityService记录
    Xilinx 7系列FPGA 高性能(HP)接口与2.5V/3.3V 外设IO接口设计考虑
    STM32 PWM配置及呼吸灯
    【c++】constexpr关键字
    2024年java面试--mysql(3)
    JavaScript中的基础知识挑战
    基于JavaWeb+SSM+Vue“鼻护灵”微信小程序系统的设计和实现
    使用C/C++实现字典树(数组或者链表方式)
    React中memo()、useCallback()、useMemo() 区别使用详解
    使用Selenium爬取目标网站被识别的解决之法
  • 原文地址:https://blog.csdn.net/qq_17347313/article/details/125588451