提供任意內存的連續區域的類型安全且內存安全的表示形式。
[ System . Runtime . InteropServices . Marshalling . NativeMarshalling ( typeof ( System . Runtime . InteropServices . Marshalling . SpanMarshaller < , > ) ) ]
public readonly ref struct Span < T >
類型參數
| 參數 | 註解 |
|---|---|
| T | Span 中項的類型 |
繼承
| Object | ValueType | Span < T > |
|---|
特性
NativeMarshallingAttribute
註解
Span < T > 類型是一種 ref struct,它在棧上分配,而非託管堆上。ref struct 類型有諸多限制,以確保它們不會被提升到託管堆,其中包括:它們不能被裝箱,不能賦值給 Object 類型、dynamic 類型的變量或任何接口類型的變量,不能作為引用類型中的字段,也不能跨 await 和 yield 邊界使用。此外,調用 Equals ( Object ) 和 GetHashCode 這兩個方法會拋出 NotSupportedException。
重要:由於 Span < T > 是僅棧類型,因此它不適用於許多需要在堆上存儲對緩衝區的引用的場景。例如,進行異步方法調用的例程就是這種情況。對於此類場景,您可以使用互補的 System . Memory < T > 和 System . ReadOnlyMemory < T > 類型。
對於表示不可變或只讀結構的跨度,請使用 System . ReadOnlySpan < T >。
內存
Span < T > 表示任意內存的連續範圍。Span < T > 實例通常用於存儲數組的元素或數組的一部分。不過,與數組不同的是,Span < T > 實例可以指向託管內存、本機內存或堆棧上管理的內存。以下示例從數組創建 Span < Byte >:
// 通過一個數組創建一個 Span
byte [ ] ZJs = new byte [ 100 ];
Span < byte > ZJsSpan = new( ZJs );
byte zj = 0;
for ( int SuoYin = 0 ; SuoYin < ZJsSpan . Length ; SuoYin++ )
ZJsSpan [ SuoYin ] = zj++;
int zhsZongHe = 0;
foreach ( byte z in ZJsSpan )
zhsZongHe += z;
Console . WriteLine ( $"總和是:{zhsZongHe}" );
以下示例通過 100 個字節的原生內存創建了一個 Span < Byte > 類型的對象:
using System . Runtime . InteropServices;
// 通過原生內存(Native Memory)創建一個 Span
nint yuansheng = Marshal . AllocHGlobal ( 100 );
try
{
Span < byte > yuanshengSpan;
unsafe
{
yuanshengSpan = new Span<byte> ( yuansheng . ToPointer ( ) , 100 );
}
byte zj = 0;
for ( int zhsSuoYin = 0 ; zhsSuoYin < yuanshengSpan . Length ; zhsSuoYin++ )
yuanshengSpan [ zhsSuoYin ] = zj++;
int zhsZongHe = 0;
foreach ( byte z in yuanshengSpan )
zhsZongHe += z;
Console . WriteLine ( $"總和是:{zhsZongHe}" );
}
finally
{
Marshal . FreeHGlobal ( yuansheng );
}
以下示例使用 C# 的 stackalloc 關鍵字在棧上分配 100 字節的內存:
// 通過棧(Stack)創建一個 Span
Span < byte > duizhanSpan = stackalloc byte [ 100 ];
byte zj = 0;
for ( int zhsSuoYin = 0 ; zhsSuoYin < duizhanSpan . Length ; zhsSuoYin++ )
duizhanSpan [ zhsSuoYin ] = zj++;
int zhsZongHe = 0;
foreach ( byte z in duizhanSpan )
zhsZongHe += z;
Console . WriteLine ( $"總和是:{zhsZongHe}" );
由於 Span < T > 是對任意連續內存塊的抽象,Span < T > 類型的方法以及帶有 Span < T > 參數的方法可對任何 Span < T > 對象進行操作,無論其封裝的內存類型如何。例如,初始化跨度並計算其元素總和的各個獨立代碼段都可以重構為單一的初始化和計算方法,如下例所示:
using System . Runtime . InteropServices;
// 通過數組創建一個 Span
byte [ ] ZJs = new byte [ 100 ];
Span < byte > ShuZuSpan = new ( ZJs );
FF初始化Span ( ShuZuSpan );
Console . WriteLine ( $"總和是:{FF計算總和 ( ShuZuSpan ):N0}" );
// 通過原生內存(Native Memory)創建一個數組
var yuansheng = Marshal . AllocHGlobal ( 100 );
Span < byte > yuanshengSpan;
unsafe
{
yuanshengSpan = new Span < byte > ( yuansheng . ToPointer ( ) , 100 );
}
FF初始化Span ( yuanshengSpan );
Console . WriteLine ( $"總和是:{FF計算總和 ( yuanshengSpan ):N0}" );
Marshal . FreeHGlobal ( yuansheng );
// Create a 範圍 on the stack.
Span<byte> zhanSpan = stackalloc byte [ 100 ];
FF初始化Span ( zhanSpan );
Console . WriteLine ( $"總和是:{FF計算總和 ( zhanSpan ):N0}" );
static void FF初始化Span ( Span < byte > 範圍 )
{
byte zjZhi = 0;
for ( int zhsSuoYin = 0 ; zhsSuoYin < 範圍 . Length ; zhsSuoYin++ )
範圍 [ zhsSuoYin ] = zjZhi++;
}
static int FF計算總和 ( ReadOnlySpan < byte > 範圍 )
{
int zhsHe = 0;
foreach ( byte zj in 範圍 )
zhsHe += zj;
return zhsHe;
}
數組
當 Span < T > 包裝數組時,它可以包裝整個數組,就像在 Memory 部分的示例中那樣。由於它支持切片,Span < T > 也可以指向數組內的任何連續範圍。
以下示例創建了一個包含 10 個元素的整數數組的中間 5 個元素的切片。請注意,這段代碼將切片中每個整數的值加倍。如輸出所示,該切片所做的更改會反映在數組的值中。
int [ ] Zhss = [ 2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 , 20 ];
var Span切片 = new Span < int > ( Zhss , 2 , 5 );
for ( int zhs = 0 ; zhs < Span切片 . Length ; zhs++ )
Span切片 [ zhs ] *= 2;
foreach ( int zhs in Zhss )
Console . WriteLine ( zhs );
Console.WriteLine ( );
foreach ( int zhs in Span切片 )
Console . WriteLine ( zhs );
Slices(切片)
Span < T > 包含 Slice 方法的兩個重載,這兩個重載可從當前跨度中形成一個從指定索引開始的切片。這使得可以將 Span < T > 中的數據視為一組邏輯塊,數據處理管道的各個部分可根據需要對這些邏輯塊進行處理,且對性能的影響極小。例如,由於現代服務器協議通常是基於文本的,因此字符串和子字符串的操作尤為重要。在 String 類中,提取子字符串的主要方法是 Substring。對於依賴大量字符串操作的數據管道而言,使用該方法會帶來一些性能損耗,因為:
- 創建一個新字符串來容納子字符串。
- 將原始字符串中的一部分字符複製到新字符串中。
如下例所示,通過使用 Span < T > 或 ReadOnlySpan < T >,可以消除這種分配和複製操作:
string zfc主體 = "Content-Length: 132";
var CD = FF獲取主體長度 ( zfc主體 . AsSpan ( ) );
Console . WriteLine ( $"主體長度:{CD}" );
static int? FF獲取主體長度 ( ReadOnlySpan < char > Span字符 )
{
int zhsIndexOf冒號 = Span字符 . LastIndexOf ( ":" );
ReadOnlySpan < Char > 切片;
if ( zhsIndexOf冒號 >= 0 )
{
切片 = Span字符 [ zhsIndexOf冒號 .. ];
}
else
{
Console . WriteLine ( $"{Span字符} 沒有 “:” 存在。" );
return null;
}
if ( int . TryParse ( 切片 , out int zhs返回值 ) )
{
return zhs返回值;
}
else { return null; }
}
構造函數
重載
| 重載 | 註解 |
|---|---|
| Span < T > ( T ) | 圍繞指定的引用創建一個長度為 1 的新 Span < T > |
| Span < T > ( T [ ] ) | 在指定數組的整個範圍內創建一個新 Span < T > |
| Span < T > ( T [ ] , Int32 索引 , Int32 元素數 ) | 在指定數組的整個範圍內創建一個新 Span < T > |
| Span < T > ( void* 指針 , Int32 元素數 ) | 從指定的內存地址開始,從指定數量的 T 元素創建一個新的Span < T > 對象 |
public Span ( ref T 引用 );
public Span ( T [ ]? 數組 );
public Span ( T [ ]? 數組 , int 起始索引 , int 元素數 );
[ System . CLSCompliant ( false ) ]
public Span ( void* 指針 , int 長度 );
參數
| 參數 | 類型 | 註解 |
|---|---|---|
| 引用 | T | 任意類型的單個值(傳遞的是對其的引用),單個值的 Span |
| 數組 | T [ ]? | 任意類型的數組,對其元素引用的 Span |
| 起始索引
元素數 |
int | 當 Span 的元素只是引用 數組 中的一部分時,指定起始索引和元素數(省略元素數將引用 起始索引 後的所有元素) |
| 指針 | void* | 指向內存中指定數量的 T 元素起始地址的指針 |
| 長度 | int | 要包含在Span < T > 中的 T 元素數量 |
異常
| 異常 | 註解 |
|---|---|
| ArrayTypeMismatchException | T 是引用類型,但 數組 不是 T 類型的數組 |
| ArgumentException | 若指定 指針 和 長度,T 是引用類型或包含指針,因此無法存儲在非託管內存中 |
| ArgumentOutOfRangeException | 若指定 指針 和 長度,但長度小於 0
若指定 起始索引 和 元素數,起始索引 + 元素數 > 數組 . Length 或 起始索引 > 數組 . Length 或 數組 為 null,但 起始索引 和/或 元素數 不是 0 |
示例
以下示例演示了 Span ( ref T 引用 ) 的基礎示例,其中 T 分別是一個 int 和 一個 sring,並演示了 int 作為值類型可變;但 string 作為引用類型不可變:
int zhs = 1;
string zfc = "123";
Span < int > zhsSpan = new ( ref zhs );
Span < string > zfcSpan = new ( ref zfc );
Console . WriteLine ( zhsSpan . ToString ( ) ); // System.Span<Int32>[1]
Console . WriteLine ( string . Join ( ',' , zhsSpan . ToArray ( ) ) ); // 1
Console . WriteLine ( zfcSpan . ToString ( ) ); //System.Span<String>[1]
Console . WriteLine ( string . Join ( ',' , zfcSpan . ToArray ( ) ) ); // 123
string zfc引用 = zfc;
zhsSpan [ 0 ] = 9;
zfcSpan [ 0 ] = "321"; // 由於 String 的不可變性,此時只是修改了其引用地址(創建了一個新的 String 對象)
Console . WriteLine ( zhsSpan . ToString ( ) );
Console . WriteLine ( string . Join ( ',' , zhsSpan . ToArray ( ) ) );
Console . WriteLine ( zfcSpan . ToString ( ) );
Console . WriteLine ( string . Join ( ',' , zfcSpan . ToArray ( ) ) );
Console . WriteLine ( $"此時 zhs = {zhs};zfc = {zfc}" ); // 原 “123” 依然存在
Console . WriteLine ( $"zfc引用 = \"{zfc引用}\"" );
以下過程示範了 Span ( ref T 引用 ) 的高級示例,T 為一個交錯數組,即 T 可以是 C# 或者 程序集 能理解的任何東西:
int [ ] [ ] Zhss = [ [ 1 , 2 , 3 ] , [ 2 , 3 , 4 ] ];
Span < int [ ] [ ] > ZhssSpan = new ( ref Zhss );
Console . WriteLine ( );
for ( int z = 0 ; z < ZhssSpan [ 0 ] . Length ; z ++ )
{
Console . WriteLine ( ZhssSpan [ 0 ] [ z ] . GetType ( ) );
Console . WriteLine ( string . Join ( ',' , ZhssSpan [ 0 ] [ z ] ) );
}
以下示例演示了 Span ( T [ ] 數組 ),創建整個數組的 Span 或部分 Span:
int [ ] ZHSs = [ 1 , 2 , 3 , 4 ];
Span < int > ZHSSpan = new ( ZHSs );
foreach ( var z in ZHSSpan )
Console . WriteLine ( z . ToString ( ) );
Span < int > ZHSBFSpan = new ( ZHSs , 2 , 2 ); // 僅限 索引 2 起始的 2 個元素
foreach ( var z in ZHSBFSpan )
Console . WriteLine ( z . ToString ( ) );
以下示例創建並修改 Span 的某個元素:
string [ ] ZFCs = [ "趙" , "錢" , "孫" , "李" , "周" , "吳" ];
Span < string > zfcSpan = new ( ZFCs );
int [ ] ZHSs = [ 1 , 2 , 3 ];
Span < int > zhsSpan = new ( ZHSs );
Console . WriteLine ( "原始的字符串:" );
foreach ( var z in ZFCs )
Console.Write ( $"{z} " );
Console . WriteLine ( );
Console . WriteLine ( "原始的整數:" );
foreach ( var z in ZHSs )
Console . Write ( $"{z} " );
Console . WriteLine ( );
// 現在修改 Span 中的某個值:
zfcSpan [ 1 ] = "李";
zhsSpan [ 1 ] = 200;
// 修改後的 Span:
Console . WriteLine ( "修改後的字符串 Span:" );
foreach ( var z in zfcSpan )
Console . Write ( $"{z} " );
Console . WriteLine ( );
Console . WriteLine ( "修改後的整數 Span:" );
foreach ( var z in zhsSpan )
Console . Write ( $"{z} " );
Console . WriteLine ( );
Console . WriteLine ( "修改後的字符串數組:" );
foreach ( var z in ZFCs )
Console . Write ( $"{z} " );
Console . WriteLine ( );
Console . WriteLine ( "修改後的整數數組:" );
foreach ( var z in ZHSs )
Console . Write ( $"{z} " );
Console . WriteLine ( );
以下示例使用 指針 和 長度 參數創建 Span(需在 unsafe 環境下執行):
// 分配非託管內存
IntPtr unmanagedMemory = Marshal . AllocHGlobal ( 100 * sizeof ( int ) );
try
{
unsafe
{
void* voidPointer = unmanagedMemory . ToPointer ( );
Span<int> span = new ( voidPointer , 100 );
// 填充數據
for ( int i = 0 ; i < span . Length ; i++ )
{
span [ i ] = i * 10;
}
foreach ( var z in span )
Console . Write ( $"{z} " );
}
}
finally
{
// 必須釋放非託管內存
Marshal . FreeHGlobal ( unmanagedMemory );
}
屬性
Empty 和 IsEmpty
Empty 返回一個空的(不是 null)Span < T > 對象;IsEmpty 返回指定 Span 對象是否為 Empty。
public static Span < T > Empty { get; }
public bool IsEmpty { get; }
屬性值
| 方法 | 屬性值 | 註解 |
|---|---|---|
| Empty | Span < T > | 一個沒有元素的 Span < T > 對象 |
| IsEmpty | bool | 如果 實例 是沒有元素的(不是 null ),返回 true;否則返回 false |
示例
int [ ] ZHSs = [ 1 , 2 ];
int [ ]? ZHSnull = null;
Span < int > zhsSpan = new ( ZHSs );
bool Berkong = zhsSpan . IsEmpty;
foreach ( var z in zhsSpan )
Console . Write ( $"{z} " ); Console . WriteLine ( $"{( Berkong ? "是" : "不是" )}空的" );
Console.WriteLine ( );
Console . WriteLine ( "下面將 Span 置空:" );
zhsSpan = [ ]; // .NET 推薦簡化形式,其實就是 Span < int > . Empty
Berkong = zhsSpan . IsEmpty;
Console . WriteLine ( $"{zhsSpan . ToString ( )} {(Berkong ? "是" : "不是")}空的" );
Console . WriteLine ( "下面是以 null 數組創建的 Span:" );
Span < int > Spannull = new ( ZHSnull );
Berkong = Spannull . IsEmpty;
Console . WriteLine ( $"{Spannull . ToString ( )} {( Berkong ? "是" : "不是" )}空的" );
註解
自 null 數組和 Empty 數組創建的 Span 均為 0 元素 Span。
Span . Item [ ] 和 Span . Length
Item [ 索引 ] 返回 Span 中指定索引處的元素(引用);Length 返回 Span 的元素數(長度)。
public ref T this [ int 索引 ] { get; }
public int Length { get; }
屬性值
| 方法 | 類型 | 註解 |
|---|---|---|
| Item | T | 位於指定索引處的元素值(引用) |
| Length | Int32 | Span 實例的長度 |
異常
| 異常 | 註解 |
|---|---|
| IndexOutOfRangeException | 索引 > 實例 . Length
索引 < 0 |
示例
int [ ] ZHSs = [ 1 , 2 ];
int [ ]? ZHSnull = null;
Span < int > zhsSpan = new ( ZHSs );
Console . WriteLine ( $"自有元素的數組創建的 Span 的長度:{zhsSpan . Length}" );
for ( int z = 0 ; z < zhsSpan . Length ; z++ )
Console . WriteLine( zhsSpan[ z ] );
// 置空 Span
zhsSpan = [ ];
Console . WriteLine ( $"數組創建的 Span 被 Empty 之後的長度:{zhsSpan . Length}" );
for ( int z = 0 ; z < zhsSpan . Length ; z++ )
Console . WriteLine ( zhsSpan [ z ] );
zhsSpan = new ( ZHSnull );
Console . WriteLine ( $"空數組創建的 Span 的長度:{zhsSpan . Length}" );
for ( int z = 0 ; z < zhsSpan . Length ; z++ )
Console . WriteLine ( zhsSpan [ z ] );
方法
Span . Clear
清除此 Span < T > 對象的內容。
public void Clear ( );
備註
Clear 方法將 Span < T > 對象中的項設置為其默認值。它不會從 Span < T > 中移除項。
示例
以下示例將 Span 賦值為某個數組,但又將其清空(Clear),又將其清空(Empty),得到有元素的 Span、有元素但是默認值的 Span 和無元素的 Span:
int [ ] ZHSs = [ 1 , 2 , 3 ];
Span < int > ZHSsSpan = new ( ZHSs );
Console . WriteLine ( "未 Clear 之前:" );
foreach ( var z in ZHSsSpan )
Console.Write ( z );
Console . WriteLine ( );
ZHSsSpan . Clear ( );
Console . WriteLine ( "在 Clear 之後:" );
foreach ( var z in ZHSsSpan )
Console . Write ( z );
Console . WriteLine ( );
ZHSsSpan = [ ];
Console . WriteLine ( "在 Empty 之後:" );
foreach ( var z in ZHSsSpan )
Console . Write ( z );
Console . WriteLine ( );
備註
Clear 方法不會移除 Span 中的元素,僅是將其恢復默認值(依元素的類型)。
Span . CopyTo
將此 Span < T > 的內容複製到目標 Span < T > 中。
public void CopyTo ( Span < T > 目標 );
參數
| 參數 | 類型 | 註解 |
|---|---|---|
| 目標 | Span < T > | 欲複製的目標 Span |
異常
| 異常 | 註解 |
|---|---|
| ArgumentException | 目標 比 實例 短 |
示例
下面這個例程複製了一個 Span:
int [ ] ZHSs = [ 1 , 2 , 3 ];
Span < int > ZHSsSpan = new ( ZHSs );
Span < int > ZHSsSpan複製 = stackalloc int [ ZHSsSpan . Length ];
ZHSsSpan . CopyTo ( ZHSsSpan複製 );
Console . WriteLine ( $"源 Span:" );
foreach ( var z in ZHSsSpan )
Console . Write ( $"{z} " );
Console . WriteLine ( );
Console . WriteLine ( $"目標 Span:" );
foreach ( var z in ZHSsSpan複製 )
Console . Write ( $"{z} " );
備註
即使 實例 和 目標 重疊,此方法也會將 實例 的所有內容複製到 目標。
Span . Equals 和 Span . GetHashCode
Equals 是比較兩個 Span 是否相等的方法;GetHashCode 方法返回 實例 的哈希代碼。均不支持。
[ System . Obsolete ( "Equals ( ) on Span will always throw an exception. Use the equality operator instead." ) ]
public override bool Equals ( object? 對象 );
[ System . Obsolete ( "GetHashCode ( ) on Span will always throw an exception." ) ]
public override int GetHashCode ( );
參數
| 參數 | 類型 | 註解 |
|---|---|---|
| 對象 | object? | 不支持 |
返回值
| 方法 | 類型 | 註解 |
|---|---|---|
| Equals | bool | 不支持 |
| GetHashCode | Int32 | 不支持 |
異常
| 異常 | 註解 |
|---|---|
| NotSupportedException | 總是不支持這兩個方法 |
備註
Equals
不支持對 Equals 方法的調用。對 Equals 方法的調用會產生兩種結果之一:
- 如果 對象 是一個 Span < T >,則該方法調用會生成編譯器錯誤 CS1503:“無法從 ‘System . Span’ 轉換為‘object’。” 這是因為 Span < T > 是一個 ref struct,它不能被裝箱,因此無法轉換為 Object。
- 如果 obj 的類型不是 Span < T >,則方法調用會引發 NotSupportedException。
要比較兩個 Span < T > 對象是否相等,請使用 Equality 比較運算符。
Span . Fill
用指定值填充此跨度的元素。
public void Fill ( T 值 );
參數
| 參數 | 類型 | 註解 |
|---|---|---|
| 值 | 任意類型 | 可以填充到 Span 中的任意數據 |
示例
以下示例演示了填充某個 Span 的全部內容:
int [ ] ZHSs = [ 1 , 2 , 3 ];
Span < int > ZHSsSpan = new ( ZHSs );
ZHSsSpan . Fill ( 24 );
foreach ( var z in ZHSsSpan )
Console . WriteLine ( z );
Span . GetEnumerator
返回此 Span < T > 實例 的枚舉器。
返回值
| 類型 | 註解 |
|---|---|
| Span < T > . Enumerator | Span 的枚舉器 |
備註
無需直接調用 GetEnumerator 方法,您可以使用 C# 的 foreach 語句以及 Visual Basic 的 For Each … Next 結構來枚舉 Span < T >。
Span . Slice
從當前跨度中切分出一個切片,該切片從指定索引開始,可以具有指定長度。
重載
| 重載 | 註解 |
|---|---|
| Slice ( int 起始索引 ) | 自當前範圍 實例 的指定索引處起始的切片 |
| Slice ( int 起始索引 , int 元素數 ) | 自當前範圍 實例 的指定索引處起始的切片,具有 元素數 長度 |
public Span < T > Slice ( int 起始索引 );
public Span < T > Slice ( int 起始索引 , int 元素數 );
參數
| 參數 | 類型 | 註解 |
|---|---|---|
| 起始索引
元素數 |
int | 指定切片的起始索引,若不指定 元素數,則切片至 Span 的末尾 |
返回值
| 類型 | 註解 |
|---|---|
| Span < T > | 按照指定範圍切片 實例 後的 Span |
異常
| 異常 | 註解 |
|---|---|
| ArgumentOutOfRangeException | 起始索引 和/或 元素數 < 0
起始索引(或 + 元素數)> 實例 . Length |
示例
int [ ] ZHSs = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ];
Span < int > ZHSsSpan = ZHSs . AsSpan ( );
Span < int > Span3 = ZHSsSpan [ 3 .. ]; // .NET 推薦使用範圍運算符,實際為 ZHSsSPan . Slice ( 3 )
Span < int > Span07 = ZHSsSpan [ .. 7 ];
Span < int > Span25 = ZHSsSpan . Slice ( 2 , 5 );
Console . WriteLine ( "Span3 的元素:" );
Console . WriteLine ( string . Join ( ',' , Span3 . ToArray ( ) ) );
Console . WriteLine ( "Span25 的元素:" );
Console . WriteLine ( string . Join ( ',' , Span25 . ToArray ( ) ) );
Console . WriteLine ( "Span07 的元素:" );
Console . WriteLine ( string . Join ( ',' , Span07 . ToArray ( ) ) );
註解
起始索引 從 0 起始。
新版的 .NET 推薦使用範圍運算符替換沒有 元素數 參數或 起始索引 參數為 0 的 Slice(但不推薦替換使用元素數的 Slice,或許 Slice 更易讀):
Span [ 3 .. ] == Span . Slice ( 3 )
Span [ .. 7 ] == Span . Slice ( 0 , 7 )
Span [ 2 .. 4 ] == Span . Slice ( 2 , 2 ) // 不被推薦的替換
Slice 允許返回 Empty Span,即 起始索引(無 元素數 參數) == 實例 . Length 或 元素數 == 0。
Span . ToArray
將此跨度的內容複製到新數組中。
public T [ ] ToArray ( );
返回值
| 類型 | 註解 |
|---|---|
| T [ ] | 與 Span 實例 相同類型的數組,包含 實例 中的所有元素 |
示例
以下示例展示了 ToArray 方法的實用範圍之一,即 Span 的排序,Span 實際有 Sort 方法,但屬於擴展方法,且需要高版本的 .NET 或 .NET Core。可借用 ToArray 方法,對其返回的數組排序,再覆蓋原 Span,得到已排序的 Span:
int [ ] ZHSs = [ 10 , 32 , 23 , 74 , 56 , 65 , 7 , 38 ];
Span < int > ZHSsSpan = ZHSs . AsSpan ( );
// 僅處理 Span,排序它
ZHSs = ZHSsSpan . ToArray ( );
Array . Sort ( ZHSs );
ZHSsSpan = ZHSs . AsSpan ( );
foreach ( var z in ZHSsSpan )
Console . Write ( $"{z} " );
Console . WriteLine ( );
備註
此方法會執行堆分配,因此應儘可能避免使用。在處理數組的 API 中,堆分配是常見的。如果不存在接受 Span < T > 的替代 API 重載,那麼使用此類 API 就無法避免。
Span . ToString
返回此 Span < T > 對象的字符串表示形式。
public override string ToString ( );
返回值
| 類型 | 註解 |
|---|---|
| String | Span 實例 的字符串表示形式 |
示例
請注意 Span < char > 與其他 Span 的區別:
int [ ] ZHSs = [ 10 , 32 , 23 , 74 , 56 , 65 , 7 , 38 ];
Span < int > ZHSsSpan = ZHSs . AsSpan ( );
char [ ] ZFs = [ 'a' , 'b' , 'c' ];
Span < char > ZFsSpan = ZFs . AsSpan ( );
string [ ] ZFCs = [ "龍生" , "九子" , "皆非龍" ];
Span < string > ZFCsSpan = ZFCs . AsSpan ( );
Console . WriteLine ( $"ZFsSpan . ToString ( ) = {ZFsSpan}" );
Console . WriteLine ( $"ZFCsSpan . ToString ( ) = {ZFCsSpan . ToString ( )}" );
Console . WriteLine ( $"ZHSsSpan . ToString ( ) = {ZHSsSpan . ToString ( )}" );
備註
對於 Span < Char >,ToString 方法會返回一個 String,其中包含 Span < T > 所指向的字符。否則,它會返回一個 String,其中包含該類型的名稱以及 Span < T > 所包含的元素數量,類似下列格式:
System . Span < 元素類型 > [ 元素數 ]
Span . TryCopyTo
嘗試將當前的 Span < T > 實例複製到目標 Span < T >,並返回一個指示覆制操作是否成功的值。
public bool TryCopyTo ( Span < T > 目標 );
參數
| 類型 | 註解 |
|---|---|
| Span < T > | 欲將 實例 複製到的目標 Span |
返回值
| 類型 | 註解 |
|---|---|
| bool | 如果複製成功,返回 true,否則返回 false |
示例
bool BerCopy;
int [ ] ZHSs源 = [ 10 , 32 , 23 ] , ZHSs目標 = [ 2 , 8 , 10 ];
Span < int > ZHSsSpan源 = ZHSs源 . AsSpan ( );
Span < int > ZHSsSpan目標 = ZHSs目標 . AsSpan ( );
foreach ( var z in ZHSsSpan目標 )
Console . Write ( $"{z} " );
Console . WriteLine ( );
BerCopy = ZHSsSpan源 . TryCopyTo ( ZHSsSpan目標 );
if ( BerCopy )
{
foreach ( var z in ZHSsSpan目標 )
Console . Write ( $"{z} " );
}
else Console . WriteLine ( "複製不成功!" );
Console . WriteLine ( );
int [ ] ZHS4 = [ 4 , 4 , 4 , 4 ];
ZHSsSpan目標 = ZHS4 . AsSpan ( );
foreach ( var z in ZHSsSpan目標 )
Console . Write ( $"{z} " );
Console . WriteLine ( );
BerCopy = ZHSsSpan源 . TryCopyTo ( ZHSsSpan目標 );
if ( BerCopy )
{
foreach ( var z in ZHSsSpan目標 )
Console . Write ( $"{z} " );
}
else Console . WriteLine ( "複製不成功!" );
Console . WriteLine ( );
int [ ] ZHS2 = [ 2 , 2 ];
ZHSsSpan目標 = ZHS2 . AsSpan ( );
foreach ( var z in ZHSsSpan目標 )
Console . Write ( $"{z} " );
Console . WriteLine ( );
BerCopy = ZHSsSpan源 . TryCopyTo ( ZHSsSpan目標 );
if ( BerCopy )
{
foreach ( var z in ZHSsSpan目標 )
Console . Write ( $"{z} " );
}
else Console . WriteLine ( "複製不成功!" );
備註
TryToCopy 若要返回 true,必須滿足:
- 實例 的 T 必須與 目標 的 T 相同(否則編譯器即不通過);
- 實例 的長度必須小於等於 目標 的長度,即:
實例 . Length <= 目標 . Length
即使 實例 和 目標 重疊,此方法也會將 實例 的所有內容複製到 目標。
// 即使源和目標重疊,也能正確複製
int [ ] ZHSs = { 1 , 2 , 3 , 4 , 5 };
Span < int > yuan = array . AsSpan ( 0 , 3 ); // [ 1 , 2 , 3 ]
Span < int > mubiao = array . AsSpan ( 2 , 3 ); // [ 3 , 4 , 5 ]
// 安全複製,不會出現數據損壞
yuan . TryCopyTo ( mubiao ); // ZHSs 變為:[ 1 , 2 , 1 , 2 , 3 ]
當 TryCopyTo 返回 false 時,不會向 目標 寫入任何數據。
運算符
Equality(相等性)
返回一個 bool 值,該值指示兩個 Span < T > 對象是否相等。
public static bool operator == ( Span < T > 左 , Span < T > 右 );
參數
| 參數 | 類型 | 註解 |
|---|---|---|
| 左
右 |
Span < T > | 欲比較的 Span 內存範圍 |
返回值
| 類型 | 註解 |
|---|---|
| bool | 若兩個 Span < T > 對象相等,返回 true;否則返回 false |
示例
int [ ] array1 = [ 1 , 2 , 3 , 4 , 5 ];
int [ ] array2 = [ 1 , 2 , 3 , 4 , 5 ]; // 內容相同但引用不同
// 創建指向同一數組的 Span
Span < int > span1 = array1 . AsSpan ( );
Span < int > span2 = array1 . AsSpan ( ); // 指向同一數組
// 創建指向不同數組但內容相同的Span
Span<int> span3 = array2.AsSpan(); // 指向不同數組但內容相同
// 創建指向同一數組不同部分的 Span
Span < int > span4 = array1 . AsSpan ( 0 , 3 ); // [ 1 , 2 , 3 ]
Span < int > span5 = array1 . AsSpan ( 2 , 3 ); // [ 3 , 4 , 5 ]
Console . WriteLine ( "比較結果:" );
Console . WriteLine ( $"span1 == span2:{span1 == span2}" ); // true - 同一內存
Console . WriteLine ( $"span1 == span3:{span1 == span3}" ); // false - 不同內存
Console . WriteLine ( $"span4 == span5:{span4 == span5}" ); // false - 不同內存區域
// 內容比較(需要手動實現)
bool contentsEqual = span1 . SequenceEqual ( span3 );
Console . WriteLine ( $"span1 . SequenceEqual ( span3 ):{contentsEqual}" ); // true - 內容相同
備註
兩個 Span < T > 對象相等的條件是它們具有相同的長度,且 左 和 右 的對應元素指向相同的內存。請注意,相等性測試不會嘗試判斷內容是否相等。
Implicit(隱式)
定義數組到 Span;數組分段(Segment)到 Span 或 Span 到 ReadOnlySpan 的隱式轉換。
public static implicit operator Span < T > ( T [ ]? 數組 );
public static implicit operator Span < T > ( ArraySegment < T > 分段 );
public static implicit operator ReadOnlySpan < T > ( Span < T > span );
參數
| 參數 | 類型 | 註解 |
|---|---|---|
| 數組 | T [ ]? | 欲轉換為 Span < T > 的數組 |
| 分段 | ArraySegment < T > | 欲轉換為 Span < T > 的數組分段 |
| span | Span < T > | 欲轉換為 ReadOnlySpan < T > 的 Span |
返回值
| 方法 | 類型 | 註解 |
|---|---|---|
| public static implicit operator Span < T > ( T [ ]? 數組 );
public static implicit operator Span < T > ( ArraySegment < T > 分段 ); |
Span < T > | 與 數組 或其片段對應的跨度 |
| public static implicit operator ReadOnlySpan < T > ( Span < T > span ); | ReadOnlySpan < T > | 與當前實例對應的只讀跨度 |
示例
int [ ] ZHSs = [ 1 , 2 , 3 ];
Span < int > ZHSsSpan = ZHSs;
string zfc = "倒黴孩子!";
ReadOnlySpan < char > ZFCsSpan = zfc;
ArraySegment < int > PD = new ( ZHSs , 1 , 2 );
Span < int > ZHSsPDSpan = PD;
foreach ( var z in ZHSsSpan )
Console . Write ( $"{z} " );
Console . WriteLine ( );
foreach ( var z in ZFCsSpan )
Console . Write ( $"{z} " );
Console . WriteLine ( );
foreach ( var z in ZHSsPDSpan )
Console . Write ( $"{z} " );
Console . WriteLine ( );
Inequality(不等性)
返回一個 bool 值,該值指示兩個 Span < T > 對象是否不相等。
public static bool operator != ( Span < T > 左 , Span < T > 右 );
參數
| 參數 | 類型 | 註解 |
|---|---|---|
| 左
右 |
Span < T > | 欲比較的 Span 內存範圍 |
返回值
| 類型 | 註解 |
|---|---|
| bool | 若兩個 Span < T > 對象不等,返回 true;否則返回 false |
示例
int [ ] array1 = [ 1 , 2 , 3 , 4 , 5 ];
int [ ] array2 = [ 1 , 2 , 3 , 4 , 5 ]; // 內容相同但引用不同
// 創建指向同一數組的 Span
Span < int > span1 = array1 . AsSpan ( );
Span < int > span2 = array1 . AsSpan ( ); // 指向同一數組
// 創建指向不同數組但內容相同的Span
Span<int> span3 = array2.AsSpan(); // 指向不同數組但內容相同
// 創建指向同一數組不同部分的 Span
Span < int > span4 = array1 . AsSpan ( 0 , 3 ); // [ 1 , 2 , 3 ]
Span < int > span5 = array1 . AsSpan ( 2 , 3 ); // [ 3 , 4 , 5 ]
Console . WriteLine ( "比較結果:" );
Console . WriteLine ( $"span1 == span2:{span1 == span2}" ); // true - 同一內存
Console . WriteLine ( $"span1 == span3:{span1 == span3}" ); // false - 不同內存
Console . WriteLine ( $"span4 == span5:{span4 == span5}" ); // false - 不同內存區域
// 內容比較(需要手動實現)
bool contentsEqual = span1 . SequenceEqual ( span3 );
Console . WriteLine ( $"span1 . SequenceEqual ( span3 ):{contentsEqual}" ); // true - 內容相同
備註
兩個 Span < T > 對象不等的條件是它們具有相同的長度,且 左 和 右 的對應元素指向不相同的內存(可能是一個值)。請注意,不等性測試不會嘗試判斷內容是否相等。