C# 13 带来了 params 集合、新 Lock 类型、ref struct 突破等 11 个新特性

版本定位

适用版本:.NET 9 | C# 13 前置知识:C# 12 基础

背景

C# 13 是继 C# 12 之后的又一次重要更新。这次更新的重点是泛型系统的突破性能优化。ref struct 可以实现接口、泛型支持 ref struct 类型参数,这些改变让 C# 的类型系统更加强大。

C# 13 新特性一览

特性 简述 实用性
params 集合 params 支持 Span<T>IEnumerable<T> ⭐⭐⭐⭐⭐
新 Lock 类型 System.Threading.Lock 替代 Monitor ⭐⭐⭐⭐⭐
\e 转义序列 新的转义字符 ⭐⭐⭐
method group 自然类型 方法组推断改进 ⭐⭐⭐
ref struct 实现接口 ref struct 可以实现接口 ⭐⭐⭐⭐
allows ref struct 泛型支持 ref struct ⭐⭐⭐⭐
partial 属性 属性和索引器支持 partial ⭐⭐⭐⭐
overload 优先级 库作者可以指定重载优先级 ⭐⭐⭐
ref locals in iterators 迭代器中支持 ref 局部变量 ⭐⭐⭐
field keyword(预览) 字段关键字预览 ⭐⭐⭐⭐
隐式索引器访问 对象初始化器中使用 [^] 语法 ⭐⭐⭐⭐

特性详解

1. params 集合

之前的做法params 只支持数组

// C# 12 及之前
void PrintAll(params string[] items)
{
    foreach (var item in items)
    {
        Console.WriteLine(item);
    }
}

PrintAll("a", "b", "c");  // OK
// PrintAll([1, 2, 3]);    // 编译错误

C# 13 的做法params 支持所有集合类型

// C# 13
void PrintAll(params string[] items) { /* ... */ }
void PrintAll(params List<string> items) { /* ... */ }
void PrintAll(params ReadOnlySpan<string> items) { /* ... */ }
void PrintAll(params IEnumerable<string> items) { /* ... */ }

// 现在可以用集合表达式
PrintAll(["a", "b", "c"]);  // OK,适用于所有重载

性能优势

  • Span<T> 避免堆分配

  • 编译器优化,减少内存拷贝

2. 新 Lock 类型

之前的做法:使用 Monitorlock 语句

// C# 12 及之前
private readonly object _lock = new();

void DoWork()
{
    lock (_lock)
    {
        // 临界区
    }
}

// 或者显式使用 Monitor
void DoWorkExplicit()
{
    Monitor.Enter(_lock);
    try
    {
        // 临界区
    }
    finally
    {
        Monitor.Exit(_lock);
    }
}

C# 13 的做法:使用 System.Threading.Lock

// C# 13
private readonly Lock _lock = new();

void DoWork()
{
    lock (_lock)  // 自动使用新的 Lock 类型
    {
        // 临界区
    }
}

// 或者显式使用
void DoWorkExplicit()
{
    using var scope = _lock.EnterScope();
    // 临界区
}

优势

  • 更好的性能

  • 更清晰的语义

  • 防止 lock 对象被错误转换

3. \e 转义序列

// C# 12 及之前
char esc = '\x1b';  // 十六进制
char esc2 = '';  // Unicode

// C# 13
char esc = '\e';  // 新的转义序列

4. method group 自然类型

// C# 13 改进了方法组的类型推断
void Process<T>(Action<T> action) { /* ... */ }

// C# 12 可能需要显式类型
Process<int>(x => Console.WriteLine(x));

// C# 13 可以推断类型
Process(x => Console.WriteLine(x));  // 编译器推断 T 为 int

5. ref struct 实现接口

之前的做法:ref struct 不能实现接口

// C# 12 及之前
ref struct MyStruct
{
    public int Value;
}

// 不能实现接口
// interface IDisposable { void Dispose(); }
// ref struct MyStruct : IDisposable { ... }  // 编译错误

C# 13 的做法:ref struct 可以实现接口

// C# 13
ref struct MyStruct : IDisposable
{
    public int Value;

    public void Dispose()
    {
        // 清理资源
    }
}

// 使用
void Process<T>(T item) where T : IDisposable
{
    item.Dispose();
}

var myStruct = new MyStruct { Value = 42 };
Process(myStruct);  // OK

6. allows ref struct

之前的做法:泛型不支持 ref struct

// C# 12 及之前
void Process<T>(T item) where T : struct
{
    // 不能使用 ref struct
}

// Process<MyRefStruct>(myStruct);  // 编译错误

C# 13 的做法:泛型支持 ref struct

// C# 13
void Process<T>(T item) where T : allows ref struct
{
    // 可以使用 ref struct
}

Process<MyRefStruct>(myStruct);  // OK

7. partial 属性和索引器

// C# 13
partial class MyClass
{
    partial int Count { get; set; }
    partial int this[int index] { get; set; }
}

// 源生成器可以实现这些成员
partial class MyClass
{
    partial int Count => _internalCount;
    partial int this[int index] => _internalArray[index];
}

8. overload 优先级

// 库作者可以指定重载优先级
public class MyService
{
    [OverloadResolutionPriority(1)]
    public void Process(int value) { /* 高优先级 */ }

    [OverloadResolutionPriority(0)]
    public void Process(object value) { /* 低优先级 */ }
}

// 调用时优先选择高优先级的重载
var service = new MyService();
service.Process(42);  // 调用 Process(int),而不是 Process(object)

9. ref locals in iterators

// C# 13
IEnumerable<int> Process(Span<int> data)
{
    ref int first = ref data[0];
    first = 100;  // OK

    yield return first;
}

10. field keyword(预览)

// C# 13 预览
public class Person
{
    public string Name
    {
        get => field;
        set => field = value?.Trim() ?? string.Empty;
    }
}

11. 隐式索引器访问(Implicit indexer access from object initializers)

之前的做法:对象初始化器中不能使用索引器语法

// C# 12 及之前
var list = new List<string>();
list.Add("a");
list.Add("b");
list[list.Count] = "hello";  // 必须在对象初始化器外赋值

// 或者使用集合初始化器
var dict = new Dictionary<string, int>
{
    ["key1"] = 1,
    ["key2"] = 2
};
// 但不能在对象初始化器中使用索引器

C# 13 的做法:对象初始化器中支持隐式索引器访问

// C# 13
// 数组
var array = new string[10];
array[^1] = "last element";  // 等价于 array[array.Length - 1]

// 在对象初始化器中使用
var list = new List<string> { [^1] = "hello" };

// 与属性初始化结合
var grid = new Grid
{
    [0, 0] = "A1",
    [0, 1] = "A2",
    [1, 0] = "B1"
};

使用场景

// 集合初始化
var stack = new Stack<int>
{
    [^1] = 10,  // 压栈操作的语法糖
    [^1] = 20
};

// 矩阵初始化
var matrix = new Matrix(3, 3);
matrix[^1, ^1] = 1.0;  // 设置右下角元素

// 字符串缓冲区
var buffer = new char[100];
buffer[^1] = '\0';  // 设置结束符

实战场景

params 集合适合的场景

// 日志方法
void Log(params string[] messages)
{
    foreach (var msg in messages)
        Console.WriteLine($"[{DateTime.Now}] {msg}");
}

// 现在可以用集合表达式
Log(["Error", "Warning", "Info"]);

新 Lock 类型适合的场景

// 缓存管理
public class Cache<T>
{
    private readonly Lock _lock = new();
    private readonly Dictionary<string, T> _cache = new();

    public T GetOrAdd(string key, Func<T> factory)
    {
        lock (_lock)
        {
            if (!_cache.TryGetValue(key, out var value))
            {
                value = factory();
                _cache[key] = value;
            }
            return value;
        }
    }
}

ref struct 接口适合的场景

// 高性能解析器
ref struct JsonParser : IDisposable
{
    private ReadOnlySpan<char> _json;

    public JsonParser(ReadOnlySpan<char> json)
    {
        _json = json;
    }

    public void Dispose()
    {
        // 清理资源
    }
}

// 可以与泛型方法配合使用
void Process<T>(T parser) where T : IDisposable
{
    using (parser)
    {
        // 处理数据
    }
}

隐式索引器访问适合的场景

// 游戏开发中的网格初始化
var board = new ChessBoard
{
    [0, 0] = new Rook(Color.White),
    [0, 1] = new Knight(Color.White),
    [7, 0] = new Rook(Color.Black),
    [7, 1] = new Knight(Color.Black)
};

// 配置文件解析
var config = new Config
{
    ["database.host"] = "localhost",
    ["database.port"] = "5432",
    ["cache.enabled"] = "true"
};

// 数据结构初始化
var ringBuffer = new RingBuffer<int>(100);
ringBuffer[^1] = 42;  // 直接设置最后一个位置

迁移建议

自动迁移

C# 13 特性大部分向后兼容:

<PropertyGroup>
    <LangVersion>13</LangVersion>
</PropertyGroup>

需要注意的 breaking changes

  1. Lock 类型:如果代码检查 lock 对象的类型,可能会受影响

  2. method group:类型推断可能推断出不同的类型

  3. overload 优先级:可能改变现有代码的重载选择

  4. 隐式索引器访问:如果代码中已有类似语法,可能会产生歧义

一句话总结

C# 13 是泛型系统的突破,ref struct 实现接口和 allows ref struct 让 C# 的类型系统更加强大。


官方文档


📦 示例代码:.NET 新特性巡礼全系列配套示例代码(含 dotnet 8/9/10)

💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!

更多推荐