当前位置:首页 > C# > 正文

高效内存操作利器(C# Span 与 Memory 全面入门教程)

在现代 C# 开发中,高性能内存操作 是提升应用程序效率的关键。从 .NET Core 2.1 开始,微软引入了两个强大的类型:Span<T>Memory<T>。它们为开发者提供了安全、高效、零分配的内存访问方式,尤其适用于高频数据处理场景,如网络通信、图像处理、游戏开发等。

高效内存操作利器(C# Span<T> 与 Memory<T> 全面入门教程) C#  高性能内存操作 .NET 内存管理 第1张

什么是 Span<T>?

Span<T> 是一个 ref struct 类型,它提供对连续内存区域的类型安全、只读或可变的视图。它的最大特点是:不能在堆上分配,只能存在于栈上,因此无法被装箱、无法作为泛型参数、也无法跨 await 边界使用。

这使得 Span<T> 非常适合用于方法内部的临时内存操作,避免不必要的内存分配,从而提升性能。

Span<T> 示例

// 从数组创建 Spanint[] numbers = { 1, 2, 3, 4, 5 };Span<int> span = numbers.AsSpan();// 修改前三个元素span.Slice(0, 3).Fill(99);// 输出:99 99 99 4 5foreach (var n in numbers){    Console.Write(n + " ");}

什么是 Memory<T>?

Span<T> 不同,Memory<T> 是一个普通的结构体(非 ref struct),可以安全地存储在堆上。这意味着它可以作为类的字段、在异步方法中使用(跨越 await)、也可以作为集合的元素。

当你需要将内存视图传递给异步方法或长期持有引用时,应使用 Memory<T>。你可以通过调用 .Span 属性在需要时获取对应的 Span<T> 进行高效操作。

Memory<T> 示例

async Task ProcessDataAsync(Memory<byte> buffer){    // 在异步方法中安全使用    Span<byte> span = buffer.Span;        // 对内存进行高效处理    for (int i = 0; i < span.Length; i++)    {        span[i] = (byte)(span[i] ^ 0xFF); // 取反    }        await Task.Delay(100); // 模拟异步操作}// 调用byte[] data = new byte[10];await ProcessDataAsync(data.AsMemory());

Span<T> 与 Memory<T> 的核心区别

特性 Span<T> Memory<T>
是否 ref struct
能否跨 await 使用 ❌ 不可以 ✅ 可以
能否作为类字段 ❌ 不可以 ✅ 可以
性能 极高(零分配) 高(少量开销)

适用场景建议

  • 如果你在写一个同步方法,并且只需要在方法内部操作一段内存(比如解析字节流、字符串切片),优先使用 Span<T>
  • 如果你需要将内存视图传递给异步方法、事件回调、或者存储在对象中,请使用 Memory<T>
  • 两者都支持从数组、字符串、栈分配(stackalloc)甚至非托管内存创建,极大提升了 .NET 内存管理 的灵活性。

总结

Span<T>Memory<T> 是现代 C# 中实现 C# Span<T>C# Memory<T> 高效内存操作的核心工具。掌握它们的使用,不仅能写出更安全的代码,还能显著提升程序性能,特别是在对延迟敏感或资源受限的环境中。

记住:能用 Span<T> 就用它;如果不行(比如需要异步),就退而求其次使用 Memory<T>。这是 .NET 高性能编程的最佳实践之一。

希望这篇教程能帮助你轻松理解并应用这两个强大的类型!