Span<T> 和 Memory<T> 是现代 .NET 类型,用于高效地处理连续内存(数组、字符串、缓冲区)— 无需分配或复制。它们通过在现有内存中提供视图而不是创建新副本来启用高性能代码,从而减少 GC 压力。
问题:切片会创建副本(分配)
text = ;
sub = text.Substring();
ReadOnlySpan<> span = text.AsSpan();
Span<T> 和 Memory<T> 是现代 .NET 类型,用于高效地处理连续内存(数组、字符串、缓冲区)— 无需分配或复制。它们通过在现有内存中提供视图而不是创建新副本来启用高性能代码,从而减少 GC 压力。
text = ;
sub = text.Substring();
ReadOnlySpan<> span = text.AsSpan();
诸如 Substring 或数组切片之类的操作会分配新对象并复制数据。Span<T> 改为提供对现有内存的视图(指针 + 长度)— 切片不需要任何成本,也不会分配任何东西。
int[] numbers = { 1, 2, 3, 4, 5 };
Span<int> slice = numbers.AsSpan(1, 3); // {2, 3, 4} — a VIEW, no copy
slice[0] = 99; // modifies the ORIGINAL array (it's a view)
// parse without allocating substrings
ReadOnlySpan<char> input = "123,456".AsSpan();
int first = int.Parse(input.Slice(0, 3)); // parse a slice, no substring allocation
Span<T> 让您可以对数组/字符串/缓冲区进行切片和处理,无需分配 — 这对热点路径(解析、序列化、处理大数据)至关重要。
Span<T> is a ref struct — it can ONLY live on the stack (not on the heap, not in
async methods, not as a class field). This guarantees safety but limits where it's used.
// Memory<T> is like Span<T> but CAN be a field / used across await (heap-storable)
async Task ProcessAsync(Memory<byte> buffer) // works in async (Span<T> can't)
{
await stream.ReadAsync(buffer);
Span<byte> span = buffer.Span; // get a Span from it when you need to process
}
Memory<T> 在需要存储视图或在异步代码中使用的场景中补充 Span<T>(其中 Span<T> 的仅限栈特性不适用)。
Span<T> 和 Memory<T> 是现代 .NET 中用于高性能、无分配代码的重要特性,理解它们对于性能关键型应用是有价值的高级知识。
它们的核心价值在于通过在现有内存中提供视图而不是分配新副本来实现对连续内存(数组、字符串、缓冲区)的高效工作 — 这很重要,因为分配会产生 GC 压力,在热点路径(解析、序列化、处理大数据、网络缓冲区)中避免分配会显著提高性能。
通常会分配内存的操作(Substring、数组切片)可以使用 spans 进行无分配操作,从而减少内存使用和 GC 开销。
理解 Span<T>(数组/字符串/缓冲区的零复制切片)、它的仅限栈约束(它是 ref struct,因此不能是类字段或在异步方法中使用 — 一个关键限制)和 Memory<T>(可以存储并在异步代码中使用的补充)对于编写高性能 C# 很重要。
这些类型在现代 .NET 自身的性能关键代码中被广泛使用,并越来越多地在库和应用程序中使用,其中无分配、高效的内存处理很重要。
虽然对于日常业务逻辑不是必需的,但理解 Span<T>/Memory<T> — 它们的作用(无分配内存视图)、它们如何帮助(减少分配和 GC 压力)及其约束 — 对于性能敏感的 C# 开发(解析、序列化、高吞吐量系统)很有价值,区分能够编写高度高效 .NET 代码的开发人员,是与性能关键的高级角色相关的主题。