1. 空值(Null)与可空类型处理

Type?(可空值类型 / Nullable Types)

  • 说明:C# 中的基本值类型(如 int, bool, double, DateTime, struct 等)默认是不允许为 null 的。在类型后面加上 ?,可以将其提升为“可空值类型”,使其能够赋予 null 值(本质上是 Nullable<T> 的语法糖)。

  • 代码示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 普通值类型:不能赋值 null,编译直接报错
    // int age = null;

    // 可空值类型:可以赋值 null(常用于数据库中的 Nullable 字段或未填项)
    int? age = null;
    DateTime? birthDate = null;

    if (age.HasValue)
    {
    Console.WriteLine($"年龄是:{age.Value}");
    }
    else
    {
    Console.WriteLine("年龄未填写");
    }

?.?[](Null 条件运算符)

  • 说明:在访问对象的属性、方法或索引器之前先检查对象是否为 null。如果是 null 则直接返回 null,不会抛出 NullReferenceException 异常。

  • 代码示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 传统写法
    string name = null;
    if (user != null)
    {
    name = user.Name;
    }

    // 语法糖写法
    string name = user?.Name;
    int? count = list?[0]?.Count;

??(Null 合并运算符)

  • 说明:如果左侧不为 null 则返回左侧的值;如果左侧为 null 则返回右侧的默认值(常用于兜底)。

  • 代码示例

    1
    2
    3
    4
    5
    // 如果 username 为 null,则使用 "默认游客"
    string displayName = username ?? "默认游客";

    // 配合可空值类型使用:如果 age 为 null,就取 18
    int finalAge = age ?? 18;

??=(Null 合并赋值运算符)

  • 说明:当且仅当左侧变量为 null 时,才将右侧的值赋给它。

  • 代码示例

    1
    2
    // 如果 list 为 null,就初始化一个新的 List;如果不为 null,保持原样
    list ??= new List<string>();

!(Null 包容 / 警告抑制运算符)

  • 说明:告诉编译器“我确定这里绝对不会是 null,不必报 null 警告”(仅影响编译期,运行时若真为 null 仍会报错,需谨慎使用)。

  • 代码示例

    1
    string text = GetNullableText()!; // 告诉编译器“不用担心,返回值绝对不是 null”

2. 类型检查与安全转换:isas

这两个关键字都用于安全的类型转换或判断,主要区别如下:

关键字 主要作用 转换失败结果 使用场景
is 检查对象是否匹配某种类型或模式 返回false 逻辑判断(if条件)
as 尝试转换为指定引用类型或可空类型 返回null(不抛异常) 需要直接获取转换后的对象

is 的用法(配合模式匹配)

  • 代码示例
1
2
3
4
5
6
7
8
9
10
11
12
13
object obj = "Hello C#";

// 基础检查
if (obj is string) { ... }

// 现代写法:模式匹配(检查的同时声明并赋值新变量 str,最推荐)
if (obj is string str)
{
Console.WriteLine(str.Length); // 直接使用 str,无需二次强转
}

// 搭配 not 关键字(C# 9.0+)
if (obj is not int) { ... }

as 的用法

  • 代码示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    object obj = "Hello C#";

    // 强转成功返回实例,失败返回 null
    string str = obj as string;

    if (str != null)
    {
    Console.WriteLine(str.Length);
    }

PS:在 C# 7.0 及以上版本中,优先推荐使用 if (obj is Type t) 模式匹配,比 as 更加直观且安全。

3. using 的多重语法糖与用法

using 关键字在 C# 中不仅能用来引入命名空间,更核心的功能是管理资源释放(自动调用 Dispose )以及类型别名定义

using 声明(C# 8.0+ 简化语法)

  • 说明:传统的 using (...) { } 代码块需要一层嵌套的大括号。在 C# 8.0 及以上版本中,可以在变量声明前直接加 using 关键字,该变量会在当前作用域结束时自动释放,大幅减少了大括号嵌套。

  • 代码示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 传统写法:多层大括号,代码向右倾斜
    using (var stream = new FileStream("test.txt", FileMode.Open))
    {
    using (var reader = new StreamReader(stream))
    {
    string content = reader.ReadToEnd();
    } // reader 在这里被 Dispose
    } // stream 在这里被 Dispose

    // C# 8.0+ 语法糖:无需大括号,离开当前方法/作用域时自动按相反顺序释放
    using var stream = new FileStream("test.txt", FileMode.Open);
    using var reader = new StreamReader(stream);
    string content = reader.ReadToEnd();
    // 方法结束时,reader 和 stream 依次自动释放

② 全局 using 指令(Global Using,C# 10.0+)

  • 说明:在任意 .cs 文件顶部写上 global using,该命名空间就会作用于整个项目里的所有文件,不再需要在每个 .cs 文件开头反复写一堆相同的 using 引用。

  • 代码示例

    1
    2
    3
    4
    5
    6
    // 在项目的任意一个文件(如 GlobalUsings.cs)中集中声明:
    global using System;
    global using System.Collections.Generic;
    global using System.Linq;

    // 这样在项目的其他所有 .cs 文件中,都不需要再写上面这几行引用了,直接用!

using 别名(C# 12.0+ 强力增强)

  • 说明:不仅可以给复杂的类名起简短别名,从 C# 12 开始,还可以给任何类型(包括元组、数组、指针等)起别名,非常适合用来简化长类型签名。

  • 代码示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 1. 解决同名类冲突或简化长类型
    using WinFormButton = System.Windows.Forms.Button;

    // 2. C# 12+:给复杂元组起别名(相当于轻量级的 struct)
    using Point3D = (int X, int Y, int Z);

    class Program
    {
    void Test()
    {
    Point3D point = (10, 20, 30);
    Console.WriteLine(point.X); // 直接访问成员
    }
    }

4. 常用前缀与后缀符号

$(字符串插值)

  • 说明:用于简化字符串拼接,直接在字符串内通过 {} 引用变量。

  • 代码示例

    1
    2
    string name = "Tom";
    string msg = $"Hello, {name}!"; // 替代 string.Format

@(逐字字符串 / 关键字转义)

  • 说明:不仅可以用来禁用字符串转义字符,还可以将 C# 保留关键字强行作为变量名使用。

  • 代码示例

    1
    2
    3
    4
    5
    // 1. 无需写路径的双反斜杠 \\
    string path = @"C:\Users\Admin\Desktop";

    // 2. 强行将 event 关键字作为变量名
    string @event = "Notice";

_(弃元 / Discard)

  • 说明:显式声明不关心的返回值或参数,避免定义无用变量。

  • 代码示例

    1
    2
    3
    4
    5
    // 不需要 out 的第二个结果
    bool success = int.TryParse("123", out _);

    // 元组解构时忽略某个值
    var (name, _) = GetUserInfo();

5. 其它高效关键字与符号

default(默认值表达)

  • 说明:获取类型的默认值(引用类型为 null,数值型为 0,布尔型为 false)。

  • 代码示例

    1
    2
    3
    // C# 7.1+ 支持无类型默认值字面量
    int number = default; // 0
    string text = default; // null

nameof(表达式名称字符串化)

  • 说明:将变量、类、属性或方法的名字转为字符串。在后续重构代码重命名时,编译器会自动更新该名称,避免硬编码字符串写错的问题。

  • 代码示例

    1
    2
    3
    4
    5
    if (user == null) 
    {
    // 输出 "user" 字符串,比直接硬编码 "user" 更稳健
    throw new ArgumentNullException(nameof(user));
    }

::(命名空间别名限定符)

  • 说明:解决不同命名空间下类名冲突的问题。

  • 代码示例

    1
    2
    3
    4
    using MyAlias = System.IO;

    // 使用 :: 明确指定别名下的类型
    MyAlias::Path.GetExtension("file.txt");