可空类型与模式匹配
可空值类型
值类型默认不能为 null。Nullable<T> 结构体(简写为 T?)让值类型可以承载空值语义。
CSHARP
int? a = 42;
int? b = null;
bool? flag = null;
double? price = 9.99;
Console.WriteLine(a.HasValue);
Console.WriteLine(b.HasValue);
Console.WriteLine(flag.GetValueOrDefault());
Console.WriteLine(price.GetValueOrDefault(0.0));
TEXT
True
False
False
9.99
示例
CSHARP
int? score = null;
Console.WriteLine(score.HasValue);
Console.WriteLine(score.GetValueOrDefault());
Console.WriteLine(score.GetValueOrDefault(60));
score = 88;
Console.WriteLine(score.Value);
TEXT
False
0
60
88
空合并与空条件运算符
?? 提供默认值,?. 安全访问成员,! 告诉编译器"此处不为 null"。
CSHARP
int? x = null;
int y = x ?? 0;
string? name = null;
int? len = name?.Length;
string s = null!;
Console.WriteLine(s is null);
TEXT
True
示例
CSHARP
string? input = null;
string display = input ?? "(空)";
Console.WriteLine(display);
int? length = input?.Length;
Console.WriteLine(length ?? -1);
string? city = "北京";
Console.WriteLine(city?.Length ?? 0);
TEXT
(空)
-1
2
可空引用类型
C# 8 起可用 #nullable enable 开启可空引用类型警告。string? 允许为 null,string 不允许。
CSHARP
#nullable enable
string? maybeNull = null;
string notNull = "hello";
maybeNull = null;
notNull = null!;
示例
CSHARP
#nullable enable
string Greet(string? name)
{
return name is null ? "你好,陌生人" : $"你好,{name}";
}
Console.WriteLine(Greet(null));
Console.WriteLine(Greet("小明"));
TEXT
你好,陌生人
你好,小明
null 检查策略
| 策略 | 用法 | 场景 |
|---|---|---|
| 显式判断 | if (x is null) |
需要分支逻辑 |
| 空合并 | x ?? defaultValue |
提供默认值 |
| 空条件 | x?.Member |
安全访问成员 |
示例
CSHARP
string? GetName() => null;
string? name = GetName();
if (name is null)
{
Console.WriteLine("名字为空");
}
string upper = name?.ToUpper() ?? "DEFAULT";
Console.WriteLine(upper);
TEXT
名字为空
DEFAULT
模式匹配初识
模式匹配让类型检查与变量提取合二为一,常用在 is 和 switch 中。
is 模式
CSHARP
object obj = 42;
if (obj is int i)
{
Console.WriteLine($"是整数:{i}");
}
TEXT
是整数:42
示例
CSHARP
object value = "hello";
if (value is int n)
{
Console.WriteLine(n);
}
else if (value is string s)
{
Console.WriteLine($"字符串长度:{s.Length}");
}
TEXT
字符串长度:5
类型模式与 switch
在 switch 中按类型分支并提取变量。
示例
CSHARP
string Describe(object obj)
{
return obj switch
{
int i => $"整数 {i}",
double d => $"双精度 {d}",
string s => $"字符串 \"{s}\"",
bool b => $"布尔 {b}",
null => "null",
_ => "未知类型"
};
}
Console.WriteLine(Describe(10));
Console.WriteLine(Describe(3.14));
Console.WriteLine(Describe("hi"));
Console.WriteLine(Describe(true));
TEXT
整数 10
双精度 3.14
字符串 "hi"
布尔 True
属性模式
属性模式按对象属性匹配,无需手动取值比较。
示例
CSHARP
var person = new { Name = "小明", Age = 20 };
if (person is { Age: > 18 })
{
Console.WriteLine("成年人");
}
string Category(object p) => p switch
{
{ Age: < 12 } => "儿童",
{ Age: >= 12 and < 18 } => "青少年",
{ Age: >= 18 } => "成年人",
_ => "未知"
};
Console.WriteLine(Category(person));
TEXT
成年人
成年人
记录类型 record
C# 9 引入 record,自带值语义相等比较和不可变性。
CSHARP
record Person(string Name, int Age);
示例
CSHARP
record Person(string Name, int Age);
var p1 = new Person("小红", 18);
var p2 = new Person("小红", 18);
Console.WriteLine(p1 == p2);
Console.WriteLine(ReferenceEquals(p1, p2));
Console.WriteLine(p1);
TEXT
True
False
Person { Name = 小红, Age = 18 }
with 表达式
with 基于现有记录创建副本并修改部分属性,原实例不变。
示例
CSHARP
record Student(string Name, int Age, string Grade);
var s1 = new Student("小华", 20, "A");
var s2 = s1 with { Age = 21, Grade = "A+" };
Console.WriteLine(s1);
Console.WriteLine(s2);
TEXT
Student { Name = 小华, Age = 20, Grade = A }
Student { Name = 小华, Age = 21, Grade = A+ }
元组 Tuple
元组将多个值组合为一个轻量结构,支持命名元素与解构。
示例
CSHARP
(int Id, string Name) person = (1, "小刚");
Console.WriteLine(person.Id);
Console.WriteLine(person.Name);
var coords = (X: 3.0, Y: 4.0);
double distance = Math.Sqrt(coords.X * coords.X + coords.Y * coords.Y);
Console.WriteLine(distance);
(int x, int y) = (10, 20);
Console.WriteLine($"x={x}, y={y}");
TEXT
1
小刚
5
x=10, y=20
元组解构与模式匹配
元组与模式匹配结合可优雅处理多条件分支。
示例
CSHARP
string Classify(int score, bool hasBonus) => (score, hasBonus) switch
{
(>= 90, true) => "卓越+奖励",
(>= 90, false) => "卓越",
(>= 60, true) => "及格+奖励",
(>= 60, false) => "及格",
_ => "不及格"
};
Console.WriteLine(Classify(95, true));
Console.WriteLine(Classify(75, false));
Console.WriteLine(Classify(50, true));
TEXT
卓越+奖励
及格
不及格
❓ 常见问题
Q
int? 和 int 可以直接运算吗?A 不能,需要用
.Value 或 ?? 转为非空值再运算。Q
string? 和 string 有什么区别?A
string? 允许赋值 null,string 在开启 nullable 后编译器会警告赋 null。Q record 和 class 的核心区别?
A record 基于值判等(==比较内容),class 基于引用判等(==比较地址)。
Q with 表达式能否用于 class?
A 不能,with 仅适用于 record 或 init-only 属性的结构体。
Q 元组的元素数量有上限吗?
A ValueTuple 最多 8 个类型参数,超过时用嵌套 TRest 扩展。
📖 小节
T?即Nullable<T>,值类型可为 nullHasValue/Value/GetValueOrDefault()访问可空值??、?.、!处理空值场景#nullable enable开启可空引用类型检查is模式、类型模式、属性模式简化类型判断switch表达式配合模式匹配简洁表达多分支record提供值相等与不可变性with创建记录副本并修改部分属性- 元组轻量组合多值,支持命名与解构
📝 作业
- 声明
double? price = null;,用GetValueOrDefault(99.9)获取默认值并输出 - 编写方法接收
string?参数,用?.和??返回字符串长度或-1 - 用
is模式判断object obj = 3.14;是否为double d,输出d * 2 - 定义
record Book(string Title, double Price);,创建实例并用with修改价格 - 用元组 switch 表达式实现
(int month, int day)判断是否为"元旦"或"国庆"



