System.Span<T> struct

Opmerking

In dit artikel vindt u aanvullende opmerkingen in de referentiedocumentatie voor deze API.

Het Span<T> type is een refstruct die wordt toegewezen aan de stack in plaats van aan de beheerde heap. Verwijzingsstructuurtypen hebben een aantal beperkingen om ervoor te zorgen dat ze niet kunnen worden gepromoveerd naar de beheerde heap, waaronder dat ze niet ingekapseld kunnen worden, ze kunnen niet worden toegewezen aan variabelen van het type Object, dynamic of een interfacetype, ze kunnen geen velden zijn in een referentietype en ze kunnen niet worden gebruikt over await en yield grensgebieden. Bovendien veroorzaken aanroepen van de twee methoden Equals(Object) en GetHashCode een NotSupportedException.

Belangrijk

Omdat het een stack-only type is, Span<T> is dit niet geschikt voor veel scenario's waarvoor verwijzingen naar buffers op de heap moeten worden opgeslagen. Dit geldt bijvoorbeeld voor routines die asynchrone methodeaanroepen maken. Voor dergelijke scenario's kunt u de complementaire typen System.Memory<T> en System.ReadOnlyMemory<T> gebruiken.

Voor delen die onveranderbare of 'read-only' structuren vertegenwoordigen, gebruikt u System.ReadOnlySpan<T>.

Geheugen

A Span<T> vertegenwoordigt een aaneengesloten regio van willekeurig geheugen. Een Span<T> exemplaar wordt vaak gebruikt voor het opslaan van de elementen van een matrix of een deel van een matrix. In tegenstelling tot een matrix kan een Span<T> exemplaar echter verwijzen naar beheerd geheugen, systeemeigen geheugen of geheugen dat op de stack wordt beheerd. In het volgende voorbeeld wordt een Span<Byte> van een matrix gemaakt:

// 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

In het volgende voorbeeld wordt een Span<Byte> systeemeigen geheugen van 100 bytes gemaakt:

// 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

In het volgende voorbeeld wordt het C# -trefwoord stackalloc gebruikt om 100 bytes geheugen toe te wijzen aan de stack:

// 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

Omdat Span<T> dit een abstractie is van een willekeurig geheugenblok, werken methoden van het Span<T> type en de methoden met Span<T> parameters op elk Span<T> object, ongeacht het type geheugen dat wordt ingekapseld. Elk van de afzonderlijke codesecties die het bereik initialiseren en de som van de elementen berekenen, kan bijvoorbeeld samengevoegd worden tot één initialisatie- en berekeningsmethode, zoals in het volgende voorbeeld geïllustreerd wordt.

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

Arrays

Wanneer een matrix wordt verpakt, Span<T> kan deze een hele matrix verpakken, zoals in de voorbeelden in de sectie Geheugen . Omdat het segmenteren ondersteunt, Span<T> kan het ook verwijzen naar elk aaneengesloten bereik binnen de matrix.

In het volgende voorbeeld wordt een segment gemaakt van de middelste vijf elementen van een matrix met een geheel getal van 10 elementen. Houd er rekening mee dat de code de waarden van elk geheel getal in het segment verdubbelt. Zoals in de uitvoer wordt weergegeven, worden de wijzigingen die door het bereik zijn aangebracht, weergegeven in de waarden van de matrix.

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

Segmenten

Span<T> bevat twee overloads van de Slice-methode die een deel vormen uit de huidige span die begint bij een opgegeven index. Dit maakt het mogelijk om de gegevens in een Span<T> set logische segmenten te behandelen die indien nodig kunnen worden verwerkt door delen van een pijplijn voor gegevensverwerking met minimale invloed op de prestaties. Omdat moderne serverprotocollen bijvoorbeeld vaak op tekst zijn gebaseerd, is het bewerken van tekenreeksen en subtekenreeksen bijzonder belangrijk. In de String klasse is Substringde primaire methode voor het extraheren van subtekenreeksen. Voor gegevenspijplijnen die afhankelijk zijn van uitgebreide bewerking van tekenreeksen, biedt het gebruik ervan enkele prestatiestraffen, omdat het:

  1. Hiermee maakt u een nieuwe tekenreeks voor het opslaan van de subtekenreeks.
  2. Hiermee kopieert u een subset van de tekens van de oorspronkelijke tekenreeks naar de nieuwe tekenreeks.

Deze toewijzings- en kopieerbewerking kan worden geëlimineerd met behulp van Span<T> of ReadOnlySpan<T>, zoals het volgende voorbeeld laat zien:

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