Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Note
Cet article vous offre des remarques complémentaires à la documentation de référence pour cette API.
Le Span<T> type est un ref struct qui est alloué sur la pile plutôt que dans le tas géré. Les types ref struct ont plusieurs restrictions pour s'assurer qu'ils ne peuvent pas être promus vers le tas managé, notamment qu'ils ne peuvent pas être convertis, assignés à des variables de type Object, dynamic ou à tout type d'interface, qu'ils ne peuvent pas être des champs dans un type de référence et qu'ils ne peuvent pas être utilisés entre les limites de await et yield. En outre, les appels à deux méthodes, Equals(Object) et GetHashCode, lèvent un NotSupportedException.
Importante
Étant donné qu’il s’agit d’un type stack-only, Span<T> il n’est pas adapté à de nombreux scénarios qui nécessitent le stockage de références aux mémoires tampons sur le tas. C’est vrai, par exemple, des routines qui effectuent des appels de méthode asynchrones. Pour ces scénarios, vous pouvez utiliser les types complémentaires System.Memory<T> et System.ReadOnlyMemory<T>.
Pour les étendues qui représentent des structures immuables ou en lecture seule, utilisez System.ReadOnlySpan<T>.
Mémoire
Span<T> représente une région contiguë de mémoire arbitraire. Une Span<T> instance est souvent utilisée pour contenir les éléments d’un tableau ou une partie d’un tableau. Contrairement à un tableau, toutefois, une Span<T> instance peut pointer vers la mémoire managée, la mémoire native ou la mémoire gérée sur la pile. L’exemple suivant crée un Span<Byte> à partir d’un tableau :
// 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
L’exemple suivant crée un objet Span<Byte> à partir de 100 octets de mémoire native :
// 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
L’exemple suivant utilise le mot clé stackalloc C# pour allouer 100 octets de mémoire sur la pile :
// 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
Étant donné qu’il Span<T> s’agit d’une abstraction sur un bloc de mémoire arbitraire, les méthodes du Span<T> type et des méthodes avec Span<T> des paramètres fonctionnent sur n’importe quel Span<T> objet quel que soit le type de mémoire qu’il encapsule. Par exemple, chacune des sections distinctes du code qui initialisent l’étendue et calcule la somme de ses éléments peut être refactorisée en méthodes d’initialisation et de calcul uniques, comme l’illustre l’exemple suivant :
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
Tableaux
Lorsqu’il encapsule un tableau, Span<T> il peut encapsuler un tableau entier, comme dans les exemples de la section Mémoire . Comme il prend en charge le découpage, Span<T> peut également pointer vers n’importe quelle plage contiguë dans le tableau.
L’exemple suivant crée une tranche des cinq éléments du milieu d’un tableau entier de 10 éléments. Notez que le code double les valeurs de chaque entier dans la tranche. Comme on peut le voir dans le résultat, les modifications effectuées par le segment sont reflétées dans les valeurs du tableau.
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
Tranches
Span<T> inclut deux surcharges de la méthode Slice qui créent un segment de l’étendue actuelle commençant à un indice spécifié. Cela permet de traiter les données dans un Span<T> ensemble de blocs logiques qui peuvent être traités selon les besoins d’un pipeline de traitement des données avec un impact minimal sur les performances. Par exemple, étant donné que les protocoles serveur modernes sont souvent basés sur du texte, la manipulation de chaînes et de sous-chaînes est particulièrement importante. Dans la String classe, la méthode principale d’extraction de sous-chaînes est Substring. Pour les pipelines de données qui s’appuient sur une manipulation de chaîne étendue, son utilisation offre des pénalités de performances, car elle :
- Crée une nouvelle chaîne pour contenir la sous-chaîne.
- Copie un sous-ensemble des caractères de la chaîne d’origine vers la nouvelle chaîne.
Cette opération d’allocation et de copie peut être supprimée en utilisant soit Span<T> soit ReadOnlySpan<T>, comme le montre l’exemple suivant :
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