菜鸟教程 -- 学的不仅是技术,更是梦想!

C# 教程
(追記) (追記ここまで)

C# 可空类型(Nullable)

在 C# 中,intfloatboolDateTime 等都是值类型,它们默认必须有一个值,不能为 null:

int a = null; // 编译错误:无法将 null 转换为 int

可空类型(Nullable Types) 解决了这个问题——它让值类型额外拥有一个"没有值"的状态(即 null),在处理数据库字段、API 返回值等可能缺失数据的场景中非常实用。

声明方式很简单,在类型后面加一个 ?:

int? a = null; // 合法
int? b = 42; // 合法

这里的 int?Nullable<int> 的语法糖(简写形式),两者完全等价:

Nullable&lt;int&gt; a = null; // 完整写法
int? a = null; // 简写,效果相同

下图展示了可空类型在内存中的工作方式——它用一个额外的布尔标记来记录"是否有值":

Nullable<T> 的内存结构 int(普通值类型) Value: 42 4 bytes,始终有值 包装 Nullable<int>(可空类型) HasValue Value 1 byte 4 bytes 共 5+ bytes int? x = 42; true ✓ 42 HasValue = true,Value = 42 int? y = null; false ✗ HasValue = false,访问 Value 会抛异常

单问号 ? 与双问号 ?? 的区别

C# 中与可空类型相关的两个运算符经常一起使用,但含义完全不同:

运算符 名称 用途 示例
? 可空类型修饰符 让值类型可以为 null int? i = 3; 等价于 Nullable<int> i = new Nullable<int>(3);
?? 空合并运算符(Null-Coalescing Operator) 当变量为 null 时提供默认值 int result = i ?? 0;

一个简单的对比:

int i; // 普通值类型,默认值为 0,永远不能是 null
int? ii; // 可空类型,默认值为 null

可空类型的声明与赋值

声明语法

<data_type>? <variable_name> = null;

例如:

int? age = null;
double? temperature = 36.6;
bool? isActive = new bool?(); // 显式构造,默认为 null
DateTime? birthday = null;

Nullable<T> 可以表示其基础值类型的正常范围,再加上一个 null 值。例如 Nullable<int> 可以是 -2,147,483,6482,147,483,647 之间的任意整数,或者 null

完整示例

实例

using System;

namespace NullableDemo
{
class Program
{
static void Main(string[] args)
{
// 声明不同类型的可空变量
int? num1 = null;
int? num2 = 45;
double? num3 = new double?(); // null
double? num4 = 3.14157;
bool? boolVal = new bool?(); // null

// 显示值(null 会显示为空字符串)
Console.WriteLine($"num1 = {num1 ?? 0}"); // 0
Console.WriteLine($"num2 = {num2 ?? 0}"); // 45
Console.WriteLine($"num3 = {num3 ?? 0.0}"); // 0
Console.WriteLine($"num4 = {num4 ?? 0.0}"); // 3.14157
Console.WriteLine($"boolVal = {boolVal}"); // (空)
}
}
}

输出结果:

num1 = 0
num2 = 45
num3 = 0
num4 = 3.14157
boolVal =

Null 合并运算符(??)

?? 运算符用于为可空类型或引用类型提供一个兜底默认值。当左侧不为 null 时返回左侧值,否则返回右侧值:

<表达式1> ?? <表达式2>
  • 如果 <表达式1> 不为 null,返回 <表达式1>;
  • 否则返回 <表达式2>

从 C# 8.0 起,还可以使用 ??=(空合并赋值运算符),仅当变量为 null 时才赋值:

int? x = null;
x ??= 10; // x 为 null,赋值为 10
x ??= 20; // x 已有值 10,不再赋值

实例

using System;

namespace NullableDemo
{
class Program
{
static void Main(string[] args)
{
double? num1 = null;
double? num2 = 3.14157;

// ?? 提供默认值
double result1 = num1 ?? 5.34; // num1 为 null → 返回 5.34
double result2 = num2 ?? 5.34; // num2 有值 → 返回 3.14157

Console.WriteLine($"num1 ?? 5.34 = {result1}"); // 5.34
Console.WriteLine($"num2 ?? 5.34 = {result2}"); // 3.14157

// 链式使用:提供多个备选值
int? a = null;
int? b = null;
int? c = 42;
int value = a ?? b ?? c ?? 0; // 依次尝试,最终得到 42
Console.WriteLine($"a ?? b ?? c ?? 0 = {value}"); // 42
}
}
}

输出结果:

num1 ?? 5.34 = 5.34
num2 ?? 5.34 = 3.14157
a ?? b ?? c ?? 0 = 42

可空类型的常用属性和方法

成员 说明 示例
.HasValue 判断变量是否有值(返回 bool) if (num.HasValue) { ... }
.Value 获取实际值(若为 null 会抛 InvalidOperationException) int x = num.Value;
.GetValueOrDefault() 安全获取值,若为 null 返回类型的默认值(如 0) num.GetValueOrDefault()
.GetValueOrDefault(T) 安全获取值,若为 null 返回指定的默认值 num.GetValueOrDefault(100)
?? 空合并运算符(语法糖,等价于 GetValueOrDefault) int result = num ?? 100;

实例

using System;

namespace NullableDemo
{
class Program
{
static void Main(string[] args)
{
int? num = null;

// HasValue + Value(传统写法)
if (num.HasValue)
Console.WriteLine($"值为: {num.Value}");
else
Console.WriteLine("num 没有值");

// 安全获取默认值
Console.WriteLine(num.GetValueOrDefault()); // 0
Console.WriteLine(num.GetValueOrDefault(99)); // 99
Console.WriteLine(num ?? 99); // 99(等价写法)

// &#x26a0; 注意:直接访问 .Value 会抛异常
// int x = num.Value; // InvalidOperationException!
}
}
}

实际应用场景

可空类型在实际开发中非常常见,尤其是处理可能缺失的数据时:

数据库字段映射

数据库中的字段允许为 NULL,用可空类型可以精确对应:

用户ID 年龄 是否激活
1 28 true
2 null false
int? age = GetUserAgeFromDB(userId: 2);
// 安全处理 null
string display = age.HasValue
 ? $"用户年龄:{age.Value}"
 : "年龄未知";
// 或者更简洁的写法
string display2 = $"用户年龄:{age ?? 0}";

可选参数与配置

// 配置项可能未设置
int? timeout = GetConfigValue("timeout");
int actualTimeout = timeout ?? 30; // 未设置时使用默认值 30 秒

链式空值检查(C# 6.0+)

// 使用 ?. 运算符安全访问可能为 null 的对象
string? name = user?.Address?.City?.Name;
// 如果 user、Address、City 中任何一个为 null,结果就是 null,不会抛异常

小结

功能 示例 说明
定义可空类型 int? x = null; 等价于 Nullable<int>
判断是否有值 x.HasValue 返回 truefalse
获取值(不安全) x.Value 若为 null 会抛 InvalidOperationException
获取默认值 x ?? 0 若为 null 返回右侧值
获取指定默认值 x.GetValueOrDefault(10) 若为 null 返回 10
空合并赋值 x ??= 10 仅当 xnull 时赋值(C# 8.0+)
安全导航访问 user?.Name usernull 则返回 null(C# 6.0+)

C# 8.0 的"可空引用类型"

从 C# 8.0 开始,引入了 可空引用类型(Nullable Reference Types),它与本文介绍的可空值类型是两套不同的机制:

对比项 可空值类型 可空引用类型
示例 int? string?
作用对象 值类型(intboolstruct 等) 引用类型(stringclass、数组等)
实现方式 运行时通过 Nullable<T> 结构体实现 编译器静态分析,不改变运行时行为
默认状态 始终存在(从 C# 1.0 起) 需在项目中启用 <Nullable>enable</Nullable>
null 检查 .HasValue 判断 编译器发出警告(非错误)

可空引用类型是编译期的安全检查工具——它不会改变程序的运行时行为,但会在代码可能产生 NullReferenceException 的地方发出编译器警告,帮助你在开发阶段就发现潜在问题。

AI 思考中...

2 篇笔记 写笔记

  1. #0

    菜鸟先飞

    137***[email protected]

    参考地址

    459
    num3 = num1 ?? 5.34; // num1 如果为空值则返回 5.34
    Console.WriteLine("num3 的值: {0}", num3);
    num3 = num2 ?? 5.34;
    Console.WriteLine("num3 的值: {0}", num3);

    C# 中两个问号的作用是判断??左边的对象是否为 null,如果不为 null 则使用 ?? 左边的对象,如果为 null 则使用 ?? 右边的对象。

    比如:

    a = b ?? c

    如果 b 为 null,则 a = c,如果 b 不为 null,则 a = b。

    菜鸟先飞

    137***[email protected]

    参考地址

    6年前 (2020年08月03日)
  2. #0

    movin2333

    mov***[email protected]

    621

    ?? 可以理解为三元运算符的简化形式:

    num3 = num1 ?? 5.34;
    num3 = (num1 == null) ? 5.34 : num1;

    movin2333

    mov***[email protected]

    6年前 (2020年12月17日)

点我分享笔记

  • 昵称 (必填)
  • 邮箱 (必填)
  • 引用地址

AltStyle によって変換されたページ (->オリジナル) /