Span<T> 結構
定義
重要
部分資訊涉及發行前產品,在發行之前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。
提供任意記憶體連續區域的類型安全與記憶體安全表示法。
generic <typename T>
public value class Span
[System.Runtime.InteropServices.Marshalling.NativeMarshalling(typeof(System.Runtime.InteropServices.Marshalling.SpanMarshaller<,>))]
public readonly ref struct Span<T>
public readonly ref struct Span<T>
[<System.Runtime.InteropServices.Marshalling.NativeMarshalling(typeof(System.Runtime.InteropServices.Marshalling.SpanMarshaller<,>))>]
type Span<'T> = struct
type Span<'T> = struct
Public Structure Span(Of T)
類型參數
- T
項目類型。Span<T>
- 繼承
- 屬性
備註
此 Span<T> 類型是一種 ref 結構 ,配置於堆疊上,而不是在受控式堆積上配置。 Ref 結構類型有一些限制,以確保它們無法被升階到受管理堆積,包括不能被提升到:
- 裝盒。
- 指派給型別 Object 為 或
dynamic的變數,或任何介面型別。 - 欄位屬於參考型態。
- 跨界
awaityield使用。
此外,呼叫兩個方法Equals(Object)和GetHashCode會擲回NotSupportedException。
Important
因為它是堆疊專用的類型,Span<T> 因此不適合許多需要在堆上儲存緩衝區參考的情境。 例如,這種情況適用於進行異步方法呼叫的例程。 針對這類案例,您可以使用互補 System.Memory<T> 和 System.ReadOnlyMemory<T> 型別。
對於代表不可變或唯讀結構之範圍,請使用 System.ReadOnlySpan<T>。
Memory
Span<T>表示任意記憶體的連續區域。
Span<T>實例通常用來保存陣列的元素或陣列的一部分。 不過,不同於陣列,Span<T> 實例可以指向受控記憶體、原生記憶體或堆疊上管理的記憶體。 下列範例會從陣列建立 Span<Byte> :
// Create a span over an array.
var array = new byte[100];
var arraySpan = new Span<byte>(array);
byte data = 0;
for (int ctr = 0; ctr < arraySpan.Length; ctr++)
arraySpan[ctr] = data++;
int arraySum = 0;
foreach (var 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
下列範例會從 100 位元組的原生記憶體建立一個 Span<Byte>:
// Create a span from native memory.
var 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 (var 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
下列範例會使用 C# stackalloc 關鍵詞在堆棧上配置 100 個字節的記憶體:
// 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 (var 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
由於 Span<T> 是任意記憶體區塊的抽象概念,因此不論其封裝的記憶體類型為何,Span<T> 類型的方法和帶有 Span<T> 參數的方法都會在任何 Span<T> 物件上執行操作。 例如,初始化範圍並計算其元素總和的程式代碼每個個別區段都可以重構成單一初始化和計算方法,如下列範例所示:
public static void WorkWithSpans()
{
// Create a span over an array.
var array = new byte[100];
var arraySpan = new Span<byte>(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(Span<byte> span)
{
int sum = 0;
foreach (var 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
陣列
當它包裝陣列時,Span<T>可以包裝整個陣列,就像記憶體區段中的範例一樣。 因為它支援切割,Span<T> 也可以指向陣列中的任何連續範圍。
下列範例會從一個有10個元素的整數陣列中,建立出中間五個元素的切片。 請注意,程式代碼會將配量中每個整數的值加倍。 如輸出所示,範圍所做的變更會反映在陣列的值中。
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
切片
Span<T> 包含兩個 Slice 方法的多載,這些多載會從當前範圍中形成一個從指定索引開始的切片。 這可讓您將Span<T>中的數據視為一組邏輯區塊,在不影響效能的情況下,根據需要處理數據處理流程的一部分。 例如,由於新式伺服器通訊協定通常是以文字為基礎的,因此對字串和子字串的操作特別重要。 在類別中 String ,擷取子字串 Substring的主要方法是 。 對於依賴大量字串操作的數據管線,其使用會造成一些效能損耗,因為它:
- 建立新的字串來保存子字串。
- 將原始字串中的字元子集複製到新的字串。
您可以使用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
建構函式
| 名稱 | Description |
|---|---|
| Span<T>(T) |
建立一個長度為 1 的新 Span<T> 參考,圍繞指定的參考。 |
| Span<T>(T[], Int32, Int32) |
建立一個新 Span<T> 物件,包含從指定索引開始的陣列元素數量。 |
| Span<T>(T[]) |
在整個指定陣列上建立一個新 Span<T> 物件。 |
| Span<T>(Void*, Int32) |
從指定記憶體位址開始的指定元素Span<T>數量建立一個新 |
屬性
| 名稱 | Description |
|---|---|
| Empty |
回傳一個空 Span<T> 物件。 |
| IsEmpty |
回傳一個表示電流 Span<T> 是否為空的值。 |
| Item[Int32] |
取得該元素在指定的零基索引處。 |
| Length |
回傳當前跨度的長度。 |
方法
| 名稱 | Description |
|---|---|
| Clear() |
清除此 Span<T> 物件的內容。 |
| CopyTo(Span<T>) | |
| Equals(Object) |
已淘汰.
已淘汰.
不支援呼叫此方法。 |
| Fill(T) |
以指定值填滿該跨度的元素。 |
| GetEnumerator() |
回傳一個列 Span<T>舉器 。 |
| GetHashCode() |
已淘汰.
|
| GetPinnableReference() |
回傳一個類型為 T 的物件參考,可用於釘選。 此方法旨在支援 .NET 編譯器,並非使用者程式碼所呼叫。 |
| Slice(Int32, Int32) |
從指定索引開始、長度指定的情況下,從當前區間中切出一個切片。 |
| Slice(Int32) |
從當前區間中劃出一個切片,起始於指定索引。 |
| ToArray() |
將此區間的內容複製到一個新的陣列中。 |
| ToString() |
回傳此 Span<T> 物件的字串表示。 |
| TryCopyTo(Span<T>) |
操作員
| 名稱 | Description |
|---|---|
| Equality(Span<T>, Span<T>) |
回傳一個表示兩個 Span<T> 物件是否相等的值。 |
| Implicit(ArraySegment<T> to Span<T>) |
定義了 的ArraySegment<T>Span<T>隱式轉換 。 |
| Implicit(Span<T> to ReadOnlySpan<T>) |
定義了 a Span<T> 隱 ReadOnlySpan<T>式轉換為 。 |
| Implicit(T[] to Span<T>) |
定義了陣列 Span<T>隱式轉換為 。 |
| Inequality(Span<T>, Span<T>) |
回傳一個表示兩個 Span<T> 物件是否不相等的值。 |