隐式类型推断

  • 说明:简化局部变量声明,让编译器根据右侧表达式自动推断类型。
  • 版本:从 C# 3.0 开始支持。
var dict = new Dictionary<string, int>
{
    ["a"] = 30,
    ["b"]   = 25
};

目标类型

  • 说明:在变量声明中可省略右侧类型。
  • 版本:从 C# 9.0 开始支持。
Dictionary<string, int> dict = new ()
{
    ["a"] = 30,
    ["b"]   = 25
};

对象初始化器

  • 版本:从 C# 3.0 开始支持。
var person = new Person { Name = "Alice", Age = 30 };

匿名类型

  • 说明:创建临时只读类型,用于存放一组相关数据。
  • 版本:从 C# 3.0 开始支持。
var anon = new { Name = "Bob", Score = 99 };
Console.WriteLine($"Name: {anon.Name}, Score: {anon.Score}");

索引与范围

  • 说明:通过 ^(从末尾索引)和 ..(切片)快速取得子序列。
  • 版本:从 C# 8.0 开始支持。
int[] a = { 1, 2, 3, 4, 5 };
Console.WriteLine(a[^1]);    // 输出 5
var slice = a[1..4];         // { 2, 3, 4 }

本地函数

  • 说明:在方法内部定义辅助函数,逻辑更内聚。
  • 版本:从 C# 7.0 开始支持。
public int Fibonacci(int n)
{
    if (n < 0) throw new ArgumentOutOfRangeException(nameof(n));
    return Fib(n).value;

    (int value, int next) Fib(int i) =>
        i == 0 ? (0, 1)
               : (Fib(i - 1).next, Fib(i - 1).value + Fib(i - 1).next);
}

Lambda 表达式

  • 版本:从 C# 3.0 开始支持。
// 无参数,返回固定值
() => 42

// 单参数,返回参数平方
x => x * x

// 多参数,要写圆括号
(x, y) => x + y

// 语句块形式
(int x, int y) =>
{
    Console.WriteLine($"Adding {x} and {y}");
    return x + y;
};

与委托类型的结合

Func<int, int, int> add = (a, b) => a + b;
int sum = add(3, 5);  // 8

Action<string> greet = name => Console.WriteLine($"Hello, {name}!");
greet("Alice");       // Hello, Alice!

对比匿名方法

// 匿名方法
Func<int, int> sq1 = delegate(int x) { return x * x; };
// Lambda 方式
Func<int, int> sq2 = x => x * x;

LINQ 中的用法

var numbers = new[] { 1, 2, 3, 4, 5 };

// 筛选偶数
var evens = numbers.Where(n => n % 2 == 0);

// 投影平方
var squares = numbers.Select(n => n * n);

// 复合查询:先筛选再排序再取前两位
var top2 = numbers
    .Where(n => n > 1)
    .OrderByDescending(n => n)
    .Take(2);

表达式树

当将 Lambda 赋给 Expression<Func<…>> 时,编译器不会直接生成可执行的 IL,而是把它编译成一棵表达式树(抽象语法树),可用于动态解析或转换成其他查询语言(如 SQL)。

Expression<Func<int, bool>> expr = x => x % 2 == 0;

// 打印表达式树结构
Console.WriteLine(expr);  
// 输出:x => ((x % 2) == 0)

表达式主体成员

  • 说明:用单行表达式替代方法或属性的完整写法,代码更简洁。
  • 版本:从 C# 6.0 开始支持。
public class MathUtil
{
    // 方法
    public static int Square(int x) => x * x;
  
    // 只读属性
    public string Info => $"Version: {Version}";
}

字符串

字符串插值

  • 语法:在字符串前加 $,然后用 {} 包裹任意表达式,编译时会将表达式求值并拼接成最终字符串。
  • 版本:从 C# 6.0 开始支持。
int a = 3, b = 5;
string s = $"Sum of {a} and {b} is {a + b}";
// 结果: "Sum of 3 and 5 is 8"

逐字字符串

  • 语法:在字符串前加 @,字符串内部不需要对反斜杠 \(文件路径)和双引号 " 进行转义;唯一需要转义的是双引号自身,用 "" 表示一个 "
string path = @"C:\Program Files\MyApp\app.exe";
// vs. 普通字符串:"C:\\Program Files\\MyApp\\app.exe"

string multiline = @"第一行
第二行
第三行";

扩展方法

  • 说明: 在不修改原类型定义的情况下,为现有类型添加方法。
  • 版本: 从 C# 3.0 开始支持。
// 定义一个扩展方法,让 string 支持 Capitalize()
public static class StringExtensions {
    public static string Capitalize(this string s) {
        if (string.IsNullOrEmpty(s)) return s;
        return char.ToUpper(s[0]) + s.Substring(1);
    }
}

// 使用
string name = "alice";
Console.WriteLine(name.Capitalize());  // 输出:Alice

模式匹配

  • 说明: 在不修改原类型定义的情况下,为现有类型添加方法。
  • 版本: 从 C# 7.0 开始支持,于 C# 11.0 完善。

优先级和顺序

模式组合器将根据表达式的绑定顺序进行排序,具体如下所示:

  • not
  • and
  • or

基础模式

直接针对单个值做最原始的判断或绑定。

  • 常量模式(Constant)is 42case 'A'
  • 类型模式(Type)is string scase MyClass mc
  • 声明模式(Var)is var x(始终匹配并绑定)
  • 空模式(Null)is nullcase null
  • 丢弃模式(Discard)is _case _
  • 关系模式(Relational, C# 9+)is > 0case <= 100
var intVar = 42;
var strVar = "Hello";
var objVar = new { Name = "Bob", Age = 42 };

if (intVar is 42)
{
    Console.WriteLine("intVar is 42");
}

if (strVar is "Hello")
{
    Console.WriteLine("strVar is Hello");
}

if (objVar is { Name: "Bob", Age: 42 })
{
    Console.WriteLine("objVar is Bob, 42");
}

// 类型模式

if (intVar is int intValue)
{
    Console.WriteLine($"intVar is an int with value {intValue}");
}

if (strVar is string strValue)
{
    Console.WriteLine($"strVar is a string with value {strValue}");
}

if (objVar is { Name: var name, Age: var age })
{
    Console.WriteLine($"objVar has Name: {name}, Age: {age}");
}

// 声明模式
if (intVar is var x) // 始终匹配并绑定
{
    Console.WriteLine($"x is {x}");
}

if (strVar is var y)
{
    Console.WriteLine($"y is {y}");
}

// 空模式
if (objVar is null)
{
    Console.WriteLine("objVar is null");
}
else
{
    Console.WriteLine("objVar is not null");
}

// 丢弃模式
if (objVar is { Name: _, Age: 42 }) // 丢弃 Name
{
    Console.WriteLine("objVar has Age 42, but we don't care about Name");
}

// 关系模式(C# 9+)
if (intVar is > 0)
{
    Console.WriteLine("intVar is greater than 0");
}

过滤与组合模式

在基础模式之上,添加逻辑组合或额外条件。

  • 模式守卫(when)case string s when s.Length>5
  • 与模式(and)is string s and not ""
  • 或模式(or)is int or long
  • 否定模式(not)is not null
var intVar = 42;
var strVar = "Hello";

// 模式守卫
switch (intVar)
{
    case int n when n > 0:
        Console.WriteLine("正整数");
        break;
    case int n when n < 0:
        Console.WriteLine("负整数");
        break;
    default:
        Console.WriteLine("零或非整数");
        break;
}
// 与模式
Console.WriteLine(Classify(13));  // output: High
Console.WriteLine(Classify(-100));  // output: Too low
Console.WriteLine(Classify(5.7));  // output: Acceptable

static string Classify(double measurement) => measurement switch
{
    < -40.0 => "Too low",
    >= -40.0 and < 0 => "Low",
    >= 0 and < 10.0 => "Acceptable",
    >= 10.0 and < 20.0 => "High",
    >= 20.0 => "Too high",
    double.NaN => "Unknown",
};

// 或模式
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));  // output: winter
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));  // output: autumn
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));  // output: spring

static string GetCalendarSeason(DateTime date) => date.Month switch
{
    3 or 4 or 5 => "spring",
    6 or 7 or 8 => "summer",
    9 or 10 or 11 => "autumn",
    12 or 1 or 2 => "winter",
    _ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),
};

// 否定模式
if (strVar is not null)
{
    // ...
}

结构化模式

类型 说明 语法 适用场景
属性模式 根据对象的属性(或字段)进行匹配 { Prop: Pattern } 匹配 C# 对象类的属性结构
位置模式 通过 Deconstruct()或元组进行按位置匹配 (Pattern1, Pattern2) 匹配元组或支持解构的类/记录类型
列表模式 按元素匹配数组、集合或 Span 的元素序列 [1, 2, .. rest] 匹配数组、List、Span 等序列型
嵌套结构 结构化模式可以嵌套使用,支持递归解构 [_, { X: 1 }] 匹配更复杂的数据结构(树形结构等)

属性模式

if (p is { X: 0, Y: var y }) { … }

位置模式

if (point is (0, 0)) { … }

列表模式

if (arr is [1, 2, .. var rest]) { … }

嵌套结构

if (mixed is [0, string s, int[] nested and [var x, var y]]) { … }

异步编程

async/await 异步编程
C# 早在 2012 年就率先在主流语言中推出同步风格的异步写法,将回调地狱彻底扭转为可读性很高的线性代码。

  • async 方法
    • 在方法签名前加 async,表示该方法内部可能包含异步操作,可使用 await
    • 必须返回 voidTaskTask<TResult> 三种之一。
    • 编译器会把方法体拆分成状态机(state machine),以支持挂起/恢复。
  • await 操作符
    • 用于“等待”一个返回 Task/Task<TResult> 的异步操作完成。
    • 执行到 await 时,若任务未完成,方法会“挂起”,返回给调用者一个尚未完成的 Task;任务完成后,再回到该处继续执行。
    • await 只能在标记了 async 的方法或 lambda 中使用。
async Task<string> FetchContentAsync(string url)
{
    using var client = new HttpClient();
    // GetStringAsync 返回 Task<string>,await 会异步等待它完成
    var result = await client.GetStringAsync(url);
    return result;
}

Console.WriteLine("开始调用异步方法");
var content = await FetchContentAsync("https://example.com");
Console.WriteLine($"获取到内容长度:{content.Length}");

编译器如何工作

  • 状态机
    • 编译器将 async 方法内部的代码分割成多个“状态”(state),每遇到一个 await 就生成一个新的状态。
    • 方法返回时,实际上是返回了一个还在运行中的状态机实例(Task)。
  • 自动生成的类型
    • 比如 FetchContentAsync 会被编译成一个私有结构体,包含字段来保存局部变量、Awaiter 对象,以及一个 MoveNext() 方法来调度各个状态。

执行流程

  1. 调用 FetchContentAsync → 生成状态机实例,执行到第一个 await
  2. await 拿到 TaskAwaiter,检查任务是否完成
    • 未完成:将当前状态记录到状态机,挂起方法,立即返回一个未完成的 Task 给调用者
    • 已完成:直接继续执行
  3. 任务完成后,调度器(默认是 SynchronizationContext 或线程池)调用状态机的 MoveNext(),恢复到上次挂起处,继续执行
  4. 最终设置返回的 Task 为已完成,并携带返回值或异常

上下文与配置

  • SynchronizationContext

    • 在 GUI(WinForms/WPF)和 ASP.NET 中,会捕获调用时的上下文,await 恢复时会回到该上下文(UI 线程或请求线程)。
  • ConfigureAwait(false)

    • 可以在库层或不关心上下文时,加上 .ConfigureAwait(false),跳过捕获上下文,继续在线程池线程上执行,提高性能并避免死锁。
string data = await client.GetStringAsync(url).ConfigureAwait(false);

异常处理

  • async Taskasync Task<TResult> 方法中抛出的异常,会被封装到返回的 Task 中,调用端使用 await 时会重新抛出该异常。
  • async void 方法中,异常无法通过 await 捕获,会直接抛到 SynchronizationContext,通常用于事件处理器,不推荐用于其他场景。
try
{
    await SomeAsyncMethod();
}
catch (Exception ex)
{
    Console.WriteLine($"捕获异常:{ex.Message}");
}