C#可空类型

文章目录

  • 一、为什么需要可空类型
  • 二、System.Nullable< T >源码
  • 三、c#对可空类型的支持
  • 三、c#的空接合操作符
  • 四、可空类型的装箱拆箱:
  • 五、可空类型的GetType()
  • 总结


一、为什么需要可空类型

  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 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 value) {
        return new Nullable(value);
    }

    [System.Runtime.Versioning.NonVersionable]
    public static explicit operator T(Nullable value) {
        return value.Value;
    }
}

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

using System;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Nullable x = 5;
            Nullable y = null;

            Console.WriteLine($"x hasvalue : {x.HasValue} Value: {x.Value}");
            Console.WriteLine($"y hasvalue : {y.HasValue} Value: {y.GetValueOrDefault()}");
        }
    }
}

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

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

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

int? x1 = 5;
int? y1 = null;

在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)
            {

            }
        }
    }
}

所有运算中,只要一个值为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;
        }
    }
}

上面代码中,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}");
        }
    }
}

C#可空类型_第1张图片
从上面结果可以看到装箱的时候需要看可空类型是不是为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}");
        }
    }
}

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

五、可空类型的GetType()

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

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

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

总结

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

你可能感兴趣的:(语言,c#,开发语言)