
C# 语法糖介绍
隐式类型推断
- 说明:简化局部变量声明,让编译器根据右侧表达式自动推断类型。
- 版本:从 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 42
、case 'A'
- 类型模式(Type):
is string s
、case MyClass mc
- 声明模式(Var):
is var x
(始终匹配并绑定) - 空模式(Null):
is null
、case null
- 丢弃模式(Discard):
is _
、case _
- 关系模式(Relational, C# 9+):
is > 0
、case <= 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
。 - 必须返回
void
、Task
或Task<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()
方法来调度各个状态。
- 比如
执行流程
- 调用
FetchContentAsync
→ 生成状态机实例,执行到第一个await
await
拿到TaskAwaiter
,检查任务是否完成- 未完成:将当前状态记录到状态机,挂起方法,立即返回一个未完成的
Task
给调用者 - 已完成:直接继续执行
- 未完成:将当前状态记录到状态机,挂起方法,立即返回一个未完成的
- 任务完成后,调度器(默认是
SynchronizationContext
或线程池)调用状态机的MoveNext()
,恢复到上次挂起处,继续执行 - 最终设置返回的
Task
为已完成,并携带返回值或异常
上下文与配置
-
SynchronizationContext
- 在 GUI(WinForms/WPF)和 ASP.NET 中,会捕获调用时的上下文,
await
恢复时会回到该上下文(UI 线程或请求线程)。
- 在 GUI(WinForms/WPF)和 ASP.NET 中,会捕获调用时的上下文,
-
ConfigureAwait(false)
- 可以在库层或不关心上下文时,加上
.ConfigureAwait(false)
,跳过捕获上下文,继续在线程池线程上执行,提高性能并避免死锁。
- 可以在库层或不关心上下文时,加上
string data = await client.GetStringAsync(url).ConfigureAwait(false);
异常处理
- 在
async Task
或async Task<TResult>
方法中抛出的异常,会被封装到返回的Task
中,调用端使用await
时会重新抛出该异常。 - 在
async void
方法中,异常无法通过await
捕获,会直接抛到SynchronizationContext
,通常用于事件处理器,不推荐用于其他场景。
try
{
await SomeAsyncMethod();
}
catch (Exception ex)
{
Console.WriteLine($"捕获异常:{ex.Message}");
}
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 Swaggy Macro
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果