Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Den här artikeln innehåller ytterligare kommentarer till referensdokumentationen för det här API:et.
Typen Span<T> är en ref struct som allokeras på stacken istället för på den hanterade högen. Referensstruktureringstyper har ett antal begränsningar för att säkerställa att de inte kan befordras till den hanterade heapen, inklusive att de inte kan boxas, att de inte kan tilldelas till variabler av typen Objecteller dynamic till någon gränssnittstyp, de kan inte vara fält i en referenstyp och de kan inte användas över await och yield gränser. Dessutom kastar anrop till två metoder, Equals(Object) och GetHashCode, en NotSupportedException.
Viktigt!
Eftersom Span<T> är en stacktyp är det olämpligt för många scenarier som kräver lagring av referenser till buffertar på heapen. Detta gäller till exempel för rutiner som gör asynkrona metodanrop. I sådana scenarier kan du använda kompletterande System.Memory<T> och System.ReadOnlyMemory<T> typer.
För intervall som representerar oföränderliga eller skrivskyddade strukturer använder du System.ReadOnlySpan<T>.
Minne
A Span<T> representerar en sammanhängande region med godtyckligt minne. En Span<T> instans används ofta för att lagra elementen i en matris eller en del av en matris. Till skillnad från en matris kan dock en Span<T> instans peka på hanterat minne, inbyggt minne eller minne som hanteras på stacken. I följande exempel skapas en Span<Byte> från en matris:
// 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
I följande exempel skapas en Span<Byte> med 100 byte ursprungligt minne:
// 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
I följande exempel används nyckelordet C# stackalloc för att allokera 100 byte minne på stacken:
// 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
Eftersom Span<T> är en abstraktion över ett godtyckligt minnesblock fungerar metoder av typen Span<T> och metoderna med Span<T> parametrar på alla Span<T> objekt oavsett vilken typ av minne det kapslar in. Till exempel kan var och en av de separata kodavsnitten som initierar intervallet och beräknar summan av dess element omstruktureras till enkla initierings- och beräkningsmetoder, vilket visas i följande exempel:
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
Matriser
När Span<T> omsluter en matris kan den omsluta en hel matris, precis som i exemplen i avsnittet Minne. Eftersom den stöder segmentering Span<T> kan den även peka på valfritt sammanhängande intervall i matrisen.
I följande exempel skapas en sektor med de fem mellersta elementen i en heltalsmatris med 10 element. Observera att koden fördubblar värdena för varje heltal i slicen. Som utdata visar återspeglas de ändringar som gjorts av intervallet i matrisens värden.
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
Segment
Span<T> innehåller två överlagringar av metoden Slice som utgör ett segment av det aktuella intervallet som börjar vid ett angivet index. Detta gör det möjligt att behandla data i en Span<T> som en uppsättning logiska segment som kan bearbetas efter behov av delar av en databearbetningspipeline med minimal prestandapåverkan. Eftersom moderna serverprotokoll ofta är textbaserade är manipulering av strängar och understrängar särskilt viktigt. I klassen String är Substring huvudmetoden för att extrahera delsträngar. För datapipelines som är beroende av omfattande strängmanipulering ger användningen vissa prestandapåföljder eftersom den:
- Skapar en ny sträng som ska innehålla delsträngen.
- Kopierar en delmängd av tecknen från den ursprungliga strängen till den nya strängen.
Den här allokerings- och kopieringsåtgärden kan elimineras med antingen Span<T> eller ReadOnlySpan<T>, som följande exempel visar:
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