.NET 9(2024.11)· \e 转义 · 方法组 · partial 属性 · 重载优先级 · 更多
ESC 字符(Unicode U+001B)是 VT100/ANSI 终端控制序列的起始字节。C# 12 只有两种方式:
// 写法 1:Unicode——安全但冗长
string red1 = "[31m红色[0m";
// 写法 2:十六进制——有歧义风险!
string red2 = "\x1b[31m红色\x1b[0m"; // \x 会贪婪匹配后续十六进制数字
// "\x1b2F" → 被解析成单个字符,而非 ESC + '2' + 'F'
// C# 13:简洁、安全、无歧义
char esc = '\e'; // 等价于 (char)0x1B
string red = "\e[31m红色\e[0m"; // ANSI 红色文字
// 终端效果展示:
Console.WriteLine("\e[1m粗体\e[0m \e[4m下划线\e[0m");
Console.WriteLine("\e[2J"); // 清屏
如果你不写终端应用,可能用不到。但写 CLI 工具、进度条、Spectre.Console 风格终端 UI 时——每天都会用上。
C# 10 起 var f = obj.Method; 可推断委托类型。但算法有 Bug——它会收集所有作用域的候选方法(包括无关扩展方法),只要有一个不匹配,全部推断失败:
// 你的类:只有一个无参 M 方法
public class C {
public void M() { }
}
// 远处某个 using 引入的扩展方法(你根本不知道它存在)
public static class SomeExtensions {
public static void M(this C c, object o) { } // 参数个数不同
}
// C# 12:编译错误 CS8917——扩展方法污染了候选集!
// var z = new C().M;
// C# 13:成功推断为 Action ✅
var z = new C().M; // 编译器先看实例方法,匹配了就停止
C# 2.0 只有 partial class 和 partial method。C# 9 增强了 partial method。但属性一直是盲区。
// C# 13:属性也能 partial 了
// 手写部分(声明):
public partial class ViewModel {
public partial string Name { get; set; }
}
// 源生成器自动产出(实现——另一个 partial 文件):
// public partial class ViewModel {
// private string _name;
// public partial string Name {
// get => _name;
// set { _name = value; OnPropertyChanged(); }
// }
// }
// 索引器同样支持
public partial int this[int index] { get; set; }
你是库作者。有旧 API,现在想加新版本。两个重载都匹配时——编译器挑哪个?
// 旧 API(保留是为了兼容)
public void Log(params object[] args) { }
// 新 API(希望编译器默认选这个)
[OverloadResolutionPriority(1)] // ← 数值越大优先级越高
public void Log(params ReadOnlySpan<object> args) { }
// 调用处:两个重载都匹配 → 编译器选优先级高的
Log(userId, operation, elapsed); // 自动选 Span 版本 ✅
| 优先级值 | 效果 | 典型场景 |
|---|---|---|
正数(如 1) | 优先选择 | 新 API——性能更好、更现代 |
0 或不加 | 默认 | 普通 API |
负数(如 -1) | 降低优先级 | 旧 API——废弃但不删除 |
C# 8 引入的 ^ 运算符(从末尾索引)现在可以在对象初始器中使用了:
// C# 12:对象初始器中不能写 ^ → 必须手动算 Length - N
var arr = new BufferHolder {
buffer = { [7] = 1, [8] = 2, [9] = 3 } // 先得知道 Length = 10
};
// C# 13:直接用 ^ 表述"从末尾"——意图一目了然
var arr = new BufferHolder {
buffer = { [^1] = 3, [^2] = 2, [^3] = 1 }
};
C# 13 以预览特性引入 field 关键字——在自动属性中直接引用编译器生成的后备字段,无需手动声明:
private string _name;
public string Name {
get => _name;
set => _name = value?.Trim()
?? throw new ArgumentNullException();
}
public string Name {
get => field;
set => field = value?.Trim()
?? throw new ArgumentNullException();
}
<LangVersion>preview</LangVersion> 并添加 <Features>field</Features>。API 和语法在最终发布前可能变动。预计在 C# 14(.NET 10)中正式发布。
这六个特性在日常编程中的出场率差别很大——诚实评估:
| 特性 | 频率 | 谁在用 | 你什么时候受益 |
|---|---|---|---|
\e 转义 | 低频 | CLI / 终端工具作者 | 写控制台应用输出彩色文字、进度条时 |
| 方法组自然类型改进 | 极低 | 修了个 Bug | 你被 CS8917 困扰过才感知到——大多数人不遇到 |
| partial 属性 | 零(你不写) | 源生成器 / 框架作者 | MVVM 框架、JSON 序列化器替你生成属性实现 |
| OverloadResolutionPriority | 极低 | 库作者 | API 演进时控制重载选择——普通项目用不上 |
^ 进对象初始器 | 偶尔 | 所有人 | 省一次心算 Length - N,代码意图更清晰 |
| field 关键字 | 高(C# 14 正式后) | 所有人 | 每个手写 get/set 的属性省一行 _field 声明 |
| 特性 | 一句话 | 影响面 | 课程 |
|---|---|---|---|
| params 集合 | params 支持 ReadOnlySpan → 零分配 | ⭐⭐⭐⭐⭐ | L18 |
| 新 Lock 对象 | object → Lock,更快更安全 | ⭐⭐⭐⭐ | L18 |
| ref struct 实现接口 | Span 可参与泛型抽象 | ⭐⭐⭐⭐ | L19 |
| allows ref struct | 泛型反约束——放行 ref struct | ⭐⭐⭐ | L19 |
| ref/unsafe 进 async | Span 可在 await 前使用 | ⭐⭐⭐ | L19 |
| \e 转义 | ESC 字符的简洁安全写法 | ⭐⭐ | 本课 |
| 方法组自然类型改进 | var 推断不再被扩展方法污染 | ⭐⭐ | 本课 |
| partial 属性 | 源生成器能生成属性了 | ⭐⭐⭐ | 本课 |
| 重载解析优先级 | 库作者控制重载选择 | ⭐ | 本课 |
| ^ 入对象初始器 | [^1] 初始化末尾元素 | ⭐ | 本课 |
| field 关键字 | 直接访问自动属性的后备字段 | 预览 | 本课 |
field 正式版、nameof 支持泛型、params 更多类型等。