认识 C# Span 走向高性能代码
in C# with 0 comment

我们都知道 使用数组的时候 就是要在堆区申请一块内存 而不用了这块内存 GC会自动回收 对于业务逻辑代码 一般不用吝啬这点性能 但是对于要频繁(数十万次及以上 每秒)申请的时候 还是会有一些的影响的
今天来学一下 Span 这个可以在堆区申请空间

stackalloc

首先讲一下 stackalloc 看到这个会不会想起 C语言的 malloc 这个是c#在栈区申请一块儿空间 但是由于是指针 所有要在 safe 语句块中

unsafe {
    int* array = stackalloc int[5]; // 在栈上分配 5 个 int
    for (int i = 0; i < 5; i++) {
        array[i] = i * i;
    }
}

Span

Span<T> 是 C# 7.2 引入的一种 安全的、栈上/堆上通用的切片结构,属于 ref struct,只能在栈上使用。

它的作用:

Span<int> span = stackalloc int[5];
for (int i = 0; i < span.Length; i++)
{
    span[i] = i * 3;
}

foreach (var item in span)
{
    Console.WriteLine(item);
}

常用方法

using System;

class Program
{
    static void Main()
    {
        // ========== 1. 创建 ==========
        int[] arr = { 1, 2, 3, 4, 5 };
        Span<int> span = arr;                   // 从数组
        Span<int> stackSpan = stackalloc int[5]; // 栈上分配
        stackSpan.Fill(7);                      // 填充 7

        Console.WriteLine("原始 span: " + string.Join(",", span.ToArray()));
        Console.WriteLine("stackalloc span: " + string.Join(",", stackSpan.ToArray()));

        // ========== 2. 属性 ==========
        Console.WriteLine($"Length={span.Length}, IsEmpty={span.IsEmpty}, 第3个={span[2]}");

        // ========== 3. 切片 ==========
        var sub1 = span.Slice(1, 3);    // [2,3,4]
        var sub2 = span[2..];           // 从第3个开始
        Console.WriteLine("Slice(1,3): " + string.Join(",", sub1.ToArray()));
        Console.WriteLine("span[2..]: " + string.Join(",", sub2.ToArray()));

        // ========== 4. 复制 ==========
        Span<int> target = new int[5];
        span.CopyTo(target);            // 复制数据
        Console.WriteLine("复制后的 target: " + string.Join(",", target.ToArray()));

        Span<int> smallTarget = new int[2];
        bool ok = span.TryCopyTo(smallTarget); // 尝试复制
        Console.WriteLine($"TryCopyTo 结果: {ok}");

        // ========== 5. 搜索 ==========
        Console.WriteLine($"IndexOf(3)={span.IndexOf(3)}");
        Console.WriteLine($"LastIndexOf(3)={span.LastIndexOf(3)}");
        Console.WriteLine($"Contains(5)={span.Contains(5)}");

        // ========== 6. 转换 ==========
        int[] arr2 = span.ToArray();    // 转换为数组
        ReadOnlySpan<int> roSpan = span; // 只读视图
        Console.WriteLine("ToArray: " + string.Join(",", arr2));
        Console.WriteLine("只读 span: " + roSpan[0]);

        // ========== 7. 比较 ==========
        Span<int> span2 = new int[] { 1, 2, 3, 4, 5 };
        Console.WriteLine("SequenceEqual=" + span.SequenceEqual(span2));

        // ========== 8. 填充 ==========
        span.Fill(9);
        Console.WriteLine("Fill(9): " + string.Join(",", span.ToArray()));

        // 注意:Span 是 ref struct,不能存到字段/类里,也不能 async/await 捕获
    }
}

优势

限制

使用场景