System.Span<T> struct

Megjegyzés:

Ez a cikk kiegészítő megjegyzéseket tartalmaz az API referenciadokumentációjához.

A Span<T> típus egy refstruktúra, amely a felügyelt halom helyett a veremben van lefoglalva. Ref struktúráknak több korlátozása van annak biztosítására, hogy ne lehessen őket a kezelt halomra emelni, így például nem dobozolhatók, nem rendelhetők hozzá Object vagy dynamic típusú változókhoz, vagy bármilyen interfész típushoz, nem lehetnek referencia típusú mezők, és nem használhatók await és yield határokon át. Emellett két metódust hív meg, Equals(Object) és GetHashCodedobjon egy NotSupportedException.

Fontos

Mivel ez egy kizárólag verem alapú típus, Span<T> nem megfelelő sok olyan helyzetben, amely a halom memóriában lévő pufferekre mutató hivatkozásokat igényel. Ez igaz például az aszinkron metódushívásokat kezdeményező rutinokra. Ilyen forgatókönyvek esetén használhatja a kiegészítő System.Memory<T> és System.ReadOnlyMemory<T> a típusokat.

A nem módosítható vagy írásvédett struktúrákat ábrázoló spanok esetében a System.ReadOnlySpan<T> használatát javasoljuk.

Memória

Az A Span<T> tetszőleges memória egy összefüggő régióját jelöli. A Span<T> példányt gyakran használják egy tömb vagy egy tömb egy részének tárolására. A tömbökkel ellentétben azonban a Span<T> példány egy felügyelt memóriára, natív memóriára vagy a veremen kezelt memóriára mutathat. Az alábbi példa egy tömbből hoz létre egy Span<Byte> példát:

// Create a span over an array.
byte[] array = new byte[100];
Span<byte> arraySpan = new(array);

byte data = 0;
for (int ctr = 0; ctr < arraySpan.Length; ctr++)
    arraySpan[ctr] = data++;

int arraySum = 0;
foreach (byte value in array)
    arraySum += value;

Console.WriteLine($"The sum is {arraySum}");

// Output:  The sum is 4950
// Create a span over an array.
let array = Array.zeroCreate<byte> 100
let arraySpan = Span<byte> array

let mutable data = 0uy
for i = 0 to arraySpan.Length - 1 do
    arraySpan[i] <- data
    data <- data + 1uy

let mutable arraySum = 0
for value in array do
    arraySum <- arraySum + int value

printfn $"The sum is {arraySum}"
// Output:  The sum is 4950

Az alábbi példa létrehoz egy Span<Byte> objektumot 100 bájtnyi natív memóriából.

// Create a span from native memory.
nint native = Marshal.AllocHGlobal(100);
Span<byte> nativeSpan;
unsafe
{
    nativeSpan = new Span<byte>(native.ToPointer(), 100);
}
byte data = 0;
for (int ctr = 0; ctr < nativeSpan.Length; ctr++)
    nativeSpan[ctr] = data++;

int nativeSum = 0;
foreach (byte value in nativeSpan)
    nativeSum += value;

Console.WriteLine($"The sum is {nativeSum}");
Marshal.FreeHGlobal(native);

// Output:  The sum is 4950
// Create a span from native memory.
let native = Marshal.AllocHGlobal 100
let nativeSpan = Span<byte>(native.ToPointer(), 100)

let mutable data = 0uy
for i = 0 to nativeSpan.Length - 1 do
    nativeSpan[i] <- data
    data <- data + 1uy

let mutable nativeSum = 0
for value in nativeSpan do
    nativeSum <- nativeSum + int value

printfn $"The sum is {nativeSum}"
Marshal.FreeHGlobal native
// Output:  The sum is 4950

Az alábbi példa a C# stackalloc kulcsszót használja 100 bájt memória lefoglalásához a veremen:

// Create a span on the stack.
byte data = 0;
Span<byte> stackSpan = stackalloc byte[100];
for (int ctr = 0; ctr < stackSpan.Length; ctr++)
    stackSpan[ctr] = data++;

int stackSum = 0;
foreach (byte value in stackSpan)
    stackSum += value;

Console.WriteLine($"The sum is {stackSum}");

// Output:  The sum is 4950
    // Create a span on the stack.
    let mutable data = 0uy
    let stackSpan = 
        let p = NativeInterop.NativePtr.stackalloc<byte> 100 |> NativeInterop.NativePtr.toVoidPtr
        Span<byte>(p, 100)

    for i = 0 to stackSpan.Length - 1 do
        stackSpan[i] <- data
        data <- data + 1uy

    let mutable stackSum = 0
    for value in stackSpan do
        stackSum <- stackSum + int value

    printfn $"The sum is {stackSum}"
// Output:  The sum is 4950

Mivel Span<T> egy absztrakció egy tetszőleges memóriablokk felett, a típus és a Span<T> paraméterekkel rendelkező Span<T> metódusok bármilyen Span<T> objektumon működnek, függetlenül attól, hogy milyen típusú memóriát foglal magában. Például a kód minden különálló szakasza, amely inicializálja a spant és kiszámítja az elemek összegét, újrabontásra kerülhet egyetlen inicializálási és számítási módszerként, ahogyan az alábbi példa is mutatja:

public static void WorkWithSpans()
{
    // Create a span over an array.
    byte[] array = new byte[100];
    Span<byte> arraySpan = new(array);

    InitializeSpan(arraySpan);
    Console.WriteLine($"The sum is {ComputeSum(arraySpan):N0}");

    // Create an array from native memory.
    var native = Marshal.AllocHGlobal(100);
    Span<byte> nativeSpan;
    unsafe
    {
        nativeSpan = new Span<byte>(native.ToPointer(), 100);
    }

    InitializeSpan(nativeSpan);
    Console.WriteLine($"The sum is {ComputeSum(nativeSpan):N0}");

    Marshal.FreeHGlobal(native);

    // Create a span on the stack.
    Span<byte> stackSpan = stackalloc byte[100];

    InitializeSpan(stackSpan);
    Console.WriteLine($"The sum is {ComputeSum(stackSpan):N0}");
}

public static void InitializeSpan(Span<byte> span)
{
    byte value = 0;
    for (int ctr = 0; ctr < span.Length; ctr++)
        span[ctr] = value++;
}

public static int ComputeSum(ReadOnlySpan<byte> span)
{
    int sum = 0;
    foreach (byte value in span)
        sum += value;

    return sum;
}

// The example displays the following output:
//    The sum is 4,950
//    The sum is 4,950
//    The sum is 4,950
open System
open System.Runtime.InteropServices
open FSharp.NativeInterop

// Package FSharp.NativeInterop.NativePtr.stackalloc for reuse.
let inline stackalloc<'a when 'a: unmanaged> length : Span<'a> =
    let voidPointer = NativePtr.stackalloc<'a> length |> NativePtr.toVoidPtr
    Span<'a>(voidPointer, length)

let initializeSpan (span: Span<byte>) =
    let mutable value = 0uy
    for i = 0 to span.Length - 1 do
        span[i] <- value
        value <- value + 1uy

let computeSum (span: Span<byte>) =
    let mutable sum = 0
    for value in span do
        sum <- sum + int value
    sum

let workWithSpans () =
    // Create a span over an array.
    let array = Array.zeroCreate<byte> 100
    let arraySpan = Span<byte> array

    initializeSpan arraySpan
    printfn $"The sum is {computeSum arraySpan:N0}"

    // Create an array from native memory.
    let native = Marshal.AllocHGlobal 100
    let nativeSpan = Span<byte>(native.ToPointer(), 100)

    initializeSpan nativeSpan
    printfn $"The sum is {computeSum nativeSpan:N0}"

    Marshal.FreeHGlobal native

    // Create a span on the stack.
    let stackSpan = stackalloc 100

    initializeSpan stackSpan
    printfn $"The sum is {computeSum stackSpan:N0}"

// The example displays the following output:
//    The sum is 4,950
//    The sum is 4,950
//    The sum is 4,950

Tömbök

Ha körbefuttat egy tömböt, Span<T> az egész tömböt körbefuttathatja, ahogyan a Memória szakaszban lévő példákban is tette. Mivel támogatja a szeletelést, Span<T> a tömb bármely összefüggő tartományára mutathat.

Az alábbi példa egy 10 elemből álló egész tömb középső öt elemének szeletét hozza létre. Vegye figyelembe, hogy a kód megduplázza a szelet egyes egészeinek értékeit. Ahogy a kimenet is mutatja, a span által végrehajtott módosítások a tömb értékeiben jelennek meg.

using System;

var array = new int[] { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };
var slice = new Span<int>(array, 2, 5);
for (int ctr = 0; ctr < slice.Length; ctr++)
    slice[ctr] *= 2;

// Examine the original array values.
foreach (var value in array)
    Console.Write($"{value}  ");
Console.WriteLine();

// The example displays the following output:
//      2  4  12  16  20  24  28  16  18  20
module Program

open System

[<EntryPoint>]
let main _ =
    let array = [| 2; 4; 6; 8; 10; 12; 14; 16; 18; 20 |]
    let slice = Span<int>(array, 2, 5)
    for i = 0 to slice.Length - 1 do
        slice[i] <- slice[i] * 2

    // Examine the original array values.
    for value in array do
        printf $"{value}  "
    printfn ""
    0
// The example displays the following output:
//      2  4  12  16  20  24  28  16  18  20

Szeletek

Span<T> két túlterhelését tartalmazza a Slice metódusnak, amelyek az aktuális szakasz egy szeletét alkotják, ami egy adott indexnél kezdődik. Ez lehetővé teszi, hogy az adatokat Span<T> logikai adattömbök halmazaként kezelje, amelyek szükség szerint feldolgozhatók egy adatfeldolgozási folyamat egyes részei által, minimális teljesítményhatással. Mivel például a modern kiszolgálói protokollok gyakran szövegalapúak, különösen fontos a sztringek és az alsztringek kezelése. String osztályban a fő módszer az alsztringek kinyerésére a Substring. A széles körű sztringmanipulálásra támaszkodó adatfolyamok használata teljesítménybeli szankciókat kínál, mivel:

  1. Létrehoz egy új karakterláncot az alsztring tárolásához.
  2. Másolja a karakterek egy részét az eredeti sztringből az új sztringbe.

Ez a lefoglalási és másolási művelet kiküszöbölhető a következő példában látható módon:Span<T>ReadOnlySpan<T>

using System;

class Program2
{
    static void Run()
    {
        string contentLength = "Content-Length: 132";
        var length = GetContentLength(contentLength.ToCharArray());
        Console.WriteLine($"Content length: {length}");
    }

    private static int GetContentLength(ReadOnlySpan<char> span)
    {
        var slice = span.Slice(16);
        return int.Parse(slice);
    }
}
// Output:
//      Content length: 132
module Program2

open System

let getContentLength (span: ReadOnlySpan<char>) =
    let slice = span.Slice 16
    Int32.Parse slice

let contentLength = "Content-Length: 132"
let length = getContentLength (contentLength.ToCharArray())
printfn $"Content length: {length}"
// Output:
//      Content length: 132