【C#学习】可空类型

C#可空类型

在程序开发中,有时候需要值类型也为可空类型,比如,在数据库中,我们可以把一个日期Datetime设置为null。

在C# 2.0中就出现了可空类型,允许值类型也可以为空(null),可空类型的实现基于C#泛型。

可空类型基本知识

可空类型的核心是System.Nullable<T>,同时静态类System.Nullable为可空类型提供了很多实用的方法。下面分别看看可空类型的这两个重要组成部分。

System.Nullable<T>

通过ILSpy我们可以查看这个类型的C#代码:

从上面的图中可以看到关于System.Nullable<T>的一些关键点:

  1. Nullable<T>是一个泛型类型
  2. 类型参数T有一个值类型的约束(根据值类型约束T : struct,T不能为可空类型,也就是说Nullable<Nullable<int>>是不允许的)
  3. Nullable<T>是一个值类型(是一个struct)

对于任何具体的可空类型来说,T的类型为可空类型的基础类型(underlying type),例如Nullable<int>的基础类型就是int。

通过上面代码还可以看到,Nullable<T>有两个重要的属性,HasValue和Value。通过它们可以了解可空类型是怎么工作的:

  1. 如果一个可空值类型存在一个真正的值,那么Value就代表这个值本身,同时HasValue值为true
  2. 如果一个可空值类型为空,那么HasValue为false,Value这是没有意义。

下面看一个可空类型的简单例子,进一步了解一下可空类型:

复制代码
static void Display(Nullable<int> x)
{
    Console.WriteLine("HasValue: {0}", x.HasValue);
    if (x.HasValue)
    {
        Console.WriteLine("Value: {0}", x.Value);
        Console.WriteLine("Explicit conversion: {0}", (int)x);
    }

    Console.WriteLine("GetValueOrDefault(): {0}", x.GetValueOrDefault());
    Console.WriteLine("GetValueOrDefault(10): {0}", x.GetValueOrDefault(10));

    Console.WriteLine("ToString(): {0}", x.ToString());
    Console.WriteLine("GetHashCode(): {0}", x.GetHashCode());
    Console.WriteLine();

}

static void Main(string[] args)
{
    Nullable<int> x = 5;
    Display(x);
    x = new Nullable<int>(9);
    Display(x);

    x = new Nullable<int>();
    Display(x);

    Console.Read();
}
复制代码

程序的输出为:

通过这段代码可以看到HasValue和Value的使用,以及Nullable<T>中一些常用的方法。

注意,在这段代码中,下面两句的IL代码是一样的:

C#代码

Nullable<int> x = 5;
x = new Nullable<int>(9);

IL代码

IL_0004: call instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_0015: call instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)

这里涉及了包装(wrapping)拆包(unwrapping)的概念:将T的一个实例转换成Nullable<T>的一个实例的过程在C#中成为包装,相反的过程成为拆包。这个概念跟装箱和拆箱不一样,后面会看到Nullable<T>的装箱和拆箱。

Nullable<T>的装箱和拆箱

从前面的分析可以看到Nullable<T>是一个结构,也就是一个值类型。也就是说,当我们把可空类型转换成一个引用类型的时候需要进行装箱操作。

对于Nullable<T>的装箱和拆箱可以概括为:

  • Nullable<T>的实例要么装箱为空引用,要么装箱成T的一个以装箱的值

  • 已装箱的值可以拆箱成普通类型,或者拆箱为对于的可空类型
    • 拆箱一个空引用时,如果拆箱为普通类型,会抛出一个NullReferenceException的异常
    • 如果拆箱成恰当的可控类型,就会拆箱成一个没有值的Nullable<T>实例

看一个关于可空类型装箱和拆箱的例子:

复制代码
static void Main(string[] args)
{
    Nullable<int> x = 5;
    //有值的可空类型装箱
    object boxed = x;
    Console.WriteLine(x.GetType());

    //拆箱为普通类型
    int normal = (int)boxed;
    Console.WriteLine(normal);

    //拆箱为可空类型
    x = (Nullable<int>)boxed;
    Console.WriteLine(x);

    x = new Nullable<int>();

    //空的可空类型装箱
    boxed = x;
    Console.WriteLine(boxed == null);

    //拆箱为可空类型
    x = (Nullable<int>)boxed;
    Console.WriteLine(x.HasValue);         
}
复制代码

输出:

System.Nullable

System.Nullable是一个静态类,只包含三个静态方法,大家可以通过ILSpy进行查看,这里就不上图了。

下面两个方法是比较方法:

public static int Compare<T>(T? n1, T? n2) where T : struct
public static bool Equals<T>(T? n1, T? n2) where T : struct

下面这个方法用来获得可空类型的基础类型:

public static Type GetUnderlyingType(Type nullableType)

可空类型语法糖

在C# 2.0中,我们可以使用?修饰符来表示可空类型。

下面的C#语句具有相同的IL代码。

Nullable<int> x = 5;
int? y = 5;
IL_0004: call instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)

IL_000d: call instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)

使用null进行赋值和比较

C#编译器允许使用null在比较和赋值中表示一个可空类型的空值。

对于下面的代码,通过IL可以发现"x == null"实际调用的是HasValue属性进行比较。

int? x = null;
Console.WriteLine(x == null);
IL_000b: call instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()

总结

C# 2.0中出现的可空类型解决了我们很多的问题,可空类型的相关知识还是比较容易理解的。

在使用中,我们可以直接使用?修饰符来创建可空值类型。

<think>嗯,用户想了解C#可空类型修饰符的用法和示例。首先,我需要回忆一下C#中的可空类型相关知识。记得C#可空类型主要用来允许值类型变量存储null值,这在处理数据库或者可选数据时特别有用。 首先,可空类型的语法是使用问号?后缀,比如int?,这实际上是Nullable<int>的简写。需要解释清楚基本语法和如何声明。然后,可能需要提到C# 8.0引入的可空引用类型,但用户的问题可能更关注值类型的可空修饰符,比如int?,不过引用类型在C# 8.0之后也有变化,但根据引用的参考内容,用户提供的引用3、5提到了可空引用类型和可空值类型,所以需要涵盖这两个方面。 接下来,用户需要具体的使用方法和示例。例如,如何声明一个可空的int,如何赋值,如何检查是否为null。可能还需要包括空合并操作符??的使用,以及安全导航操作符?.的应用,这在处理可能为null的对象时非常有用。 另外,用户提供的引用5中有示例代码,里面展示了int?的声明、空合并操作符和安全导航操作符。需要结合这些示例来组织回答,确保正确引用。比如,int? x = null;和int? y = x ?? 10;这样的例子。 还要注意可空类型和普通类型之间的转换,比如从int?到int可能需要显式转换或者使用Value属性,但要注意处理可能的InvalidOperationException异常。可以提到使用HasValue和Value属性来安全访问值。 对于可空引用类型,比如string?,需要说明在C# 8.0之后,启用可空引用上下文的情况下,引用类型默认是非空的,而加上?修饰符允许null。但需要提到编译器警告和如何处理这些警告,比如使用null宽容运算符!来明确允许null。 需要区分可空值类型和可空引用类型的不同之处。比如,可空值类型是结构体Nullable<T>,而可空引用类型是编译时的静态分析,不影响运行时类型。 可能还需要提到使用场景,比如数据库映射、可选参数、与外部API交互等,这些情况下可能需要处理null值。 还要注意在代码示例中的正确语法,比如在引用5中,使用int? x = null;,然后使用??操作符,以及安全导航操作符?.来获取可能null的属性的Length。这些示例需要正确展示,并且解释清楚。 用户可能的问题还包括如何处理可空类型的比较,或者如何将可空类型转换为普通类型,如何处理可能的null异常,比如使用GetValueOrDefault()方法。 需要确保回答结构清晰,分点解释基本语法、核心操作符、类型转换、可空引用类型,并提供示例代码。同时,根据用户提供的引用,正确标注引用标记,比如引用3、引用5等。 最后,生成的相关问题需要围绕可空类型的其他方面,比如与三目运算符的使用、性能影响、与Dapper等ORM的交互,以及如何处理可空类型的异常。这样可以帮助用户进一步深入学习。</think>### C# 可空类型修饰符使用方法与示例 #### 1. 基本语法与定义 C#可空类型修饰符 `?` 允许**值类型变量存储 `null` 值**,主要用于处理数据库查询结果、可选参数等场景[^3][^5]。 - **值类型可空**:语法为 `T?`(如 `int?`),等价于 `System.Nullable<T>` - **引用类型可空(C# 8.0+)**:语法为 `T?`(如 `string?`),需启用可空上下文(`#nullable enable`)[^3] #### 2. 核心操作符与用法 1️⃣ **空合并运算符 `??`** ```csharp int? x = null; int y = x ?? 10; // 若x为null,则返回10 ``` 2️⃣ **安全导航运算符 `?.`** ```csharp string? str = null; int? length = str?.Length; // str为null时返回null ``` 3️⃣ **类型转换** ```csharp int? a = 5; int b = a.Value; // 显式取值(需确保a非null) int c = a.GetValueOrDefault(); // 安全取值(null时返回默认值) ``` #### 3. 示例代码解析 ```csharp using System; public class Program { public static void Main() { int? x = null; // 声明可空int int? y = x ?? 20; // 空合并:x为null时y=20 string? s = null; int? len = s?.Length; // 安全导航:s为null时len=null Console.WriteLine($"x={x}, y={y}, len={len}"); // 输出:x=, y=20, len= } } ``` > 注意:直接访问 `x.Value` 若为 `null` 会抛出 `InvalidOperationException` #### 4. 可空引用类型特性(C# 8.0+) 启用方法(在.csproj中添加): ```xml <PropertyGroup> <Nullable>enable</Nullable> </PropertyGroup> ``` 示例: ```csharp #nullable enable string? nullableString = null; // 允许null string nonNullableString = null; // 编译器警告 #nullable disable ``` #### 5. 使用场景 - **数据库映射**:字段可能为 `NULL` - **API响应解析**:部分字段可能缺失 - **可选参数**:`void Log(string? message = null)` --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值