C# Span(對於開發者的運行原理)
Span是 C# 7.2 中引入的一種類型,它是 Span 的一部分。 System 命名空間中的結構體。 其設計目的在於表示任意記憶體的連續區域。 與管理堆等陣列或集合不同,Span 並不擁有堆疊記憶體或其指向的記憶體區域; 相反地,它提供了一個輕量級的檢視,超過現有的記憶區塊。 此特性使得 Span 在需要有效率地使用記憶體緩衝區,而不會產生額外開銷和不安全程式碼的情況下,功能特別強大。 在本文後面,我們還將看到 Iron Software 的 IronPDF 函式庫的介紹。
Span 的主要特性
1.記憶體管理
C# 中的 Span 可讓開發人員直接使用記憶體,而無需借助傳統的堆分配。 它提供了從現有陣列或其他記憶體來源建立記憶體切片的方法,省去了額外的記憶體副本。
2.零複製抽象
C# Span 的突出特點之一是其零複製抽象。 Span 提供了一種有效引用現有記憶體的方式,而非重複資料。 這對複製大量資料不切實際或成本過高的情況特別有利。
3.類似指標的操作
雖然 C# 傳統上是一種高階、安全的語言,但 Span 引入了一定程度的低階記憶體操作,類似於在 C 或 C++ 等語言中使用指針。 開發人員可以在不犧牲 C# 的安全性和管理性的前提下,執行類指針的操作。
4.永恆不變的本質
儘管 C# Span 具備低階記憶體存取的能力,但它仍然是不可變的。 這意味著,在允許操作記憶體的同時,還要透過防止意外修改來強制執行安全性。
範例
using System;
class Program
{
static void Main()
{
int[] array = { 1, 2, 3, 4, 5 };
// Create a span that points to the entire array
Span<int> span = array;
// Modify the data using the span
span[2] = 10;
// Print the modified array
foreach (var item in array)
{
Console.WriteLine(item);
}
}
}
using System;
class Program
{
static void Main()
{
int[] array = { 1, 2, 3, 4, 5 };
// Create a span that points to the entire array
Span<int> span = array;
// Modify the data using the span
span[2] = 10;
// Print the modified array
foreach (var item in array)
{
Console.WriteLine(item);
}
}
}
Imports System
Friend Class Program
Shared Sub Main()
Dim array() As Integer = { 1, 2, 3, 4, 5 }
' Create a span that points to the entire array
Dim span As Span(Of Integer) = array
' Modify the data using the span
span(2) = 10
' Print the modified array
For Each item In array
Console.WriteLine(item)
Next item
End Sub
End Class
只讀跨度
Span 是可變的,允許修改底層資料,而 ReadOnlySpan 是一種不可變的記憶體檢視。 它提供了連續記憶體區域的唯讀介面,因此適用於只需要讀取資料而不需要修改資料的情況。
以下是一些重點。
1.唯讀檢視
顧名思義,ReadOnlySpan允許您建立記憶體區塊的只讀視圖。 這意味著您無法透過 ReadOnlySpan 修改元素。 。
2.記憶體表示法
與 Span 相同,ReadOnlySpan 並不擁有它所指向的記憶體。 它指向現有的記憶體,可以指向陣列、堆疊分配的記憶體或本機記憶體。
3.效能優點
與 Span 相同,ReadOnlySpan 與傳統集合類型相比,可以提供更好的效能,尤其是在處理大量資料時,因為它減少了複製的需求。
4.無界限檢查
與 Span 相同,ReadOnlySpan 不執行邊界檢查。 開發人員有責任確保操作不超出底層記憶體的範圍。
5.使用陣列切片。
ReadOnlySpan 支援切片,允許您建立引用原始記憶體一部分的子範圍。
範例
using System;
class Program
{
static void Main()
{
int[] array = { 1, 2, 3, 4, 5 };
// Create a read-only span that points to the entire array
ReadOnlySpan<int> readOnlySpan = array;
// Access and print the data through the read-only span
foreach (var item in readOnlySpan)
{
Console.WriteLine(item);
}
// Note: The following line would result in a compilation error since readOnlySpan is read-only.
// readOnlySpan[2] = 10;
}
}
using System;
class Program
{
static void Main()
{
int[] array = { 1, 2, 3, 4, 5 };
// Create a read-only span that points to the entire array
ReadOnlySpan<int> readOnlySpan = array;
// Access and print the data through the read-only span
foreach (var item in readOnlySpan)
{
Console.WriteLine(item);
}
// Note: The following line would result in a compilation error since readOnlySpan is read-only.
// readOnlySpan[2] = 10;
}
}
Imports System
Friend Class Program
Shared Sub Main()
Dim array() As Integer = { 1, 2, 3, 4, 5 }
' Create a read-only span that points to the entire array
Dim readOnlySpan As ReadOnlySpan(Of Integer) = array
' Access and print the data through the read-only span
For Each item In readOnlySpan
Console.WriteLine(item)
Next item
' Note: The following line would result in a compilation error since readOnlySpan is read-only.
' readOnlySpan[2] = 10;
End Sub
End Class
有許多不同的方法可以建立 ReadOnlySpan 並使用它。 以下是一些範例。
1.從字串建立 ReadOnlySpan。
string msg = "Hello, World!";
ReadOnlySpan<char> span1 = msg.AsSpan();
// Read-only manipulation
char firstChar = span1[0];
Console.WriteLine(firstChar); // Outputs: H
string msg = "Hello, World!";
ReadOnlySpan<char> span1 = msg.AsSpan();
// Read-only manipulation
char firstChar = span1[0];
Console.WriteLine(firstChar); // Outputs: H
Dim msg As String = "Hello, World!"
Dim span1 As ReadOnlySpan(Of Char) = msg.AsSpan()
' Read-only manipulation
Dim firstChar As Char = span1(0)
Console.WriteLine(firstChar) ' Outputs: H
2.使用子串工作
在 ReadOnlySpan 使用 Slice
// Example usage of Slice method on ReadOnlySpan<char>
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ReadOnlySpan<char> substringSpan = spanFromString.Slice(7, 6); // Extracts 'String'
// Example usage of Slice method on ReadOnlySpan<char>
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ReadOnlySpan<char> substringSpan = spanFromString.Slice(7, 6); // Extracts 'String'
' Example usage of Slice method on ReadOnlySpan<char>
Dim spanFromString As ReadOnlySpan(Of Char) = "Sample String".AsSpan()
Dim substringSpan As ReadOnlySpan(Of Char) = spanFromString.Slice(7, 6) ' Extracts 'String'
3.將子串傳送至方法。
傳送 ReadOnlySpan
void ProcessSubstringfromReadOnlySpan(ReadOnlySpan<char> substring)
{
// Perform operations on the substring
}
// Usage
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(7, 6));
void ProcessSubstringfromReadOnlySpan(ReadOnlySpan<char> substring)
{
// Perform operations on the substring
}
// Usage
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(7, 6));
Private Sub ProcessSubstringfromReadOnlySpan(ByVal substring As ReadOnlySpan(Of Char))
' Perform operations on the substring
End Sub
' Usage
Private spanFromString As ReadOnlySpan(Of Char) = "Sample String".AsSpan()
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(7, 6))
4.在字串內搜尋
ReadOnlySpan<char> 在字串中搜尋 IndexOf()。
ReadOnlySpan<char> stringSpan = "Hello, World!".AsSpan();
int index = stringSpan.IndexOf('W');
Console.WriteLine(index); // Outputs: 7
ReadOnlySpan<char> stringSpan = "Hello, World!".AsSpan();
int index = stringSpan.IndexOf('W');
Console.WriteLine(index); // Outputs: 7
Dim stringSpan As ReadOnlySpan(Of Char) = "Hello, World!".AsSpan()
Dim index As Integer = stringSpan.IndexOf("W"c)
Console.WriteLine(index) ' Outputs: 7
5.使用記憶體映射檔案。
ReadOnlySpan
using System;
using System.IO.MemoryMappedFiles;
class Program
{
static void ProcessData(ReadOnlySpan<byte> data)
{
// Process data directly from the memory-mapped file
}
static void Main()
{
using (var memmf = MemoryMappedFile.CreateFromFile("data.bin"))
{
using (var accessor = memmf.CreateViewAccessor())
{
byte[] buffer = new byte[accessor.Capacity];
accessor.ReadArray(0, buffer, 0, buffer.Length);
ReadOnlySpan<byte> dataSpan = new ReadOnlySpan<byte>(buffer);
ProcessData(dataSpan);
}
}
}
}
using System;
using System.IO.MemoryMappedFiles;
class Program
{
static void ProcessData(ReadOnlySpan<byte> data)
{
// Process data directly from the memory-mapped file
}
static void Main()
{
using (var memmf = MemoryMappedFile.CreateFromFile("data.bin"))
{
using (var accessor = memmf.CreateViewAccessor())
{
byte[] buffer = new byte[accessor.Capacity];
accessor.ReadArray(0, buffer, 0, buffer.Length);
ReadOnlySpan<byte> dataSpan = new ReadOnlySpan<byte>(buffer);
ProcessData(dataSpan);
}
}
}
}
Imports System
Imports System.IO.MemoryMappedFiles
Friend Class Program
Private Shared Sub ProcessData(ByVal data As ReadOnlySpan(Of Byte))
' Process data directly from the memory-mapped file
End Sub
Shared Sub Main()
Using memmf = MemoryMappedFile.CreateFromFile("data.bin")
Using accessor = memmf.CreateViewAccessor()
Dim buffer(accessor.Capacity - 1) As Byte
accessor.ReadArray(0, buffer, 0, buffer.Length)
Dim dataSpan As New ReadOnlySpan(Of Byte)(buffer)
ProcessData(dataSpan)
End Using
End Using
End Sub
End Class
6.高效的字串操作
ReadOnlySpan
Span<char> newSpan = new char[6];
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan().Slice(7, 6);
spanFromString.CopyTo(newSpan);
Console.WriteLine(new string(newSpan)); // Outputs: String
Span<char> newSpan = new char[6];
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan().Slice(7, 6);
spanFromString.CopyTo(newSpan);
Console.WriteLine(new string(newSpan)); // Outputs: String
Dim newSpan As Span(Of Char) = New Char(5){}
Dim spanFromString As ReadOnlySpan(Of Char) = "Sample String".AsSpan().Slice(7, 6)
spanFromString.CopyTo(newSpan)
Console.WriteLine(New String(newSpan)) ' Outputs: String
7.將子串傳送至 API。
當使用外部函式庫或 API 運作字元跨度時。
void ExternalApiMethod(ReadOnlySpan<char> data)
{
// Call the external API with the character span
}
// Usage
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ExternalApiMethod(spanFromString.Slice(7, 6));
void ExternalApiMethod(ReadOnlySpan<char> data)
{
// Call the external API with the character span
}
// Usage
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ExternalApiMethod(spanFromString.Slice(7, 6));
Private Sub ExternalApiMethod(ByVal data As ReadOnlySpan(Of Char))
' Call the external API with the character span
End Sub
' Usage
Private spanFromString As ReadOnlySpan(Of Char) = "Sample String".AsSpan()
ExternalApiMethod(spanFromString.Slice(7, 6))
ReadOnlySpan
跨度限制
C# 中的 Span 功能強大,優點眾多,但也有一定的限制和注意事項,尤其是在連續記憶體和非連續記憶體的情況下。 讓我們來探討這些限制:
1.連續記憶體緩衝區。
1.1 無自動記憶體管理。
Span 不管理它所指向的記憶體。 這意味著,如果底層記憶體被釋放或超出作用域,使用 Span 進行處理將導致行為異常或潛在崩潰。 開發人員在使用 Span 時需要確保底層記憶體仍然有效。
1.2 無垃圾回收。
由於 Span 不擁有記憶體,因此不受垃圾回收機制的約束。 因此,在使用堆疊分配記憶體或生命週期短於 Span 本身的記憶體時,需要格外小心。
1.3 邊界檢查已停用。
Span 和 ReadOnlySpan 預設不執行邊界檢查。 如果不小心使用,可能會導致存取無效的記憶體位置。 開發人員需要手動確保對 Span 的操作保持在底層記憶體的範圍內。
1.4 不支援非連續記憶體。
Span 旨在與連續記憶體配合使用。 如果您的記憶體不連續或需要表示更複雜的資料結構,Span 可能不是最適合的選擇。
1.5 並非所有操作都支援。
Span 雖然支援許多常見操作(如切片、索引和迭代),但並非所有操作都受到支援。 例如,您無法調整 Span 的大小,且不允許進行某些涉及改變底層記憶體長度的操作。
1.6有限的平台相容性
Span 雖然是 .NET Standard 和 .NET Core 的一部分,但可能並非在所有平台或環境中都可以使用。 如果您打算在程式碼中使用 Span,確保目標平台支援它至關重要。
2.非連續記憶體緩衝區
2.1 對非連續記憶體的有限支援
ReadOnlySpan 主要設計用於與連續的記憶體區塊或緩衝區無縫協作。 對於涉及非連續記憶體緩衝區或記憶體有空隙的結構的場景,可能不是最適合的選擇。
2.2結構限制
某些依賴非連續記憶體的資料結構或場景可能與 ReadOnlySpan 不相容。 。 例如,由於 ReadOnlySpan 對連續記憶體的要求,像鍊錶或圖結構這樣的資料結構可能不太適用。 。
2.3複雜指標操作
在涉及非連續記憶體的情況下,尤其是在需要複雜指標運算的情況下,ReadOnlySpan 非常有用。可能無法提供像 C++ 等語言中的原始指標那樣的底層控制和靈活性。 在這種情況下,利用不安全代碼與指針可能更為合適。
2.4 某些 API 缺乏直接支援。
與連續記憶體類似,需要注意的是,並非所有 API 或函式庫都直接支援由 ReadOnlySpan 表示的非連續記憶體。 。 為了適應這些情況,可能需要額外的中間步驟或轉換,以確保相容性。
跨和非管理記憶體
在 C# 中,Span 可以有效地與非管理記憶體搭配使用,以受控且有效率的方式執行與記憶體相關的作業。 非管理記憶體指的是未被 .NET runtime 的垃圾回收器管理的記憶體,它通常涉及使用本機記憶體分配和取消分配。 以下是 Span 如何利用 C# 中的非管理記憶體。
分配非管理記憶體
若要分配非管理記憶體,您可以使用 System.Runtime.InteropServices.MemoryMarshal 類別。 Marshal.AllocHGlobal 方法會分配記憶體,並傳回已分配區塊的指標。 分配的記憶體或記憶體位址會以非管理記憶體指針 (unmanagedMemory pointer) 的方式持有,並會具有讀寫存取權。 可輕鬆存取記憶體的連續區域。
using System;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
const int bufferSize = 100;
IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
// Create a Span from the unmanaged memory
Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
// Use the Span as needed...
// Don't forget to free the unmanaged memory when done
Marshal.FreeHGlobal(unmanagedMemory);
}
}
using System;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
const int bufferSize = 100;
IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
// Create a Span from the unmanaged memory
Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
// Use the Span as needed...
// Don't forget to free the unmanaged memory when done
Marshal.FreeHGlobal(unmanagedMemory);
}
}
Imports System
Imports System.Runtime.InteropServices
Friend Class Program
Shared Sub Main()
Const bufferSize As Integer = 100
Dim unmanagedMemory As IntPtr = Marshal.AllocHGlobal(bufferSize)
' Create a Span from the unmanaged memory
Dim span As New Span(Of Byte)(unmanagedMemory.ToPointer(), bufferSize)
' Use the Span as needed...
' Don't forget to free the unmanaged memory when done
Marshal.FreeHGlobal(unmanagedMemory)
End Sub
End Class
在上述程式碼中,我們使用 Marshal.AllocHGlobal 分配了一塊非託管記憶體,然後使用從非託管記憶體中獲得的指標建立了 Span<byte>。 這可讓我們使用熟悉的 Span API 來處理非管理記憶體。 值得注意的是,在使用非管理記憶體時,您必須負責管理記憶體的分配和取消分配。
將資料複製到非管理記憶體或從非管理記憶體複製資料。
Span 提供了諸如 Slice、CopyTo 和 ToArray 之類的方法,可用於在託管記憶體和非託管記憶體之間高效複製資料。
using System;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
// Managed array to copy data from
int[] sourceArray = { 1, 2, 3, 4, 5 };
// Allocate unmanaged memory for the destination data
IntPtr destinationPointer = Marshal.AllocHGlobal(sourceArray.Length * sizeof(int));
try
{
// Create a Span<int> from the source array
Span<int> sourceSpan = sourceArray;
// Create a Span<int> from the allocated unmanaged memory
Span<int> destinationSpan = new Span<int>(destinationPointer.ToPointer(), sourceArray.Length);
// Copy data from the source Span<int> to the destination Span<int>
sourceSpan.CopyTo(destinationSpan);
// Print the values in the destination memory
Console.WriteLine("Values in the destination memory:");
foreach (var value in destinationSpan)
{
Console.Write($"{value} ");
}
}
finally
{
// Deallocate the unmanaged memory when done
Marshal.FreeHGlobal(destinationPointer);
}
}
}
using System;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
// Managed array to copy data from
int[] sourceArray = { 1, 2, 3, 4, 5 };
// Allocate unmanaged memory for the destination data
IntPtr destinationPointer = Marshal.AllocHGlobal(sourceArray.Length * sizeof(int));
try
{
// Create a Span<int> from the source array
Span<int> sourceSpan = sourceArray;
// Create a Span<int> from the allocated unmanaged memory
Span<int> destinationSpan = new Span<int>(destinationPointer.ToPointer(), sourceArray.Length);
// Copy data from the source Span<int> to the destination Span<int>
sourceSpan.CopyTo(destinationSpan);
// Print the values in the destination memory
Console.WriteLine("Values in the destination memory:");
foreach (var value in destinationSpan)
{
Console.Write($"{value} ");
}
}
finally
{
// Deallocate the unmanaged memory when done
Marshal.FreeHGlobal(destinationPointer);
}
}
}
Imports System
Imports System.Runtime.InteropServices
Friend Class Program
Shared Sub Main()
' Managed array to copy data from
Dim sourceArray() As Integer = { 1, 2, 3, 4, 5 }
' Allocate unmanaged memory for the destination data
Dim destinationPointer As IntPtr = Marshal.AllocHGlobal(sourceArray.Length * Len(New Integer()))
Try
' Create a Span<int> from the source array
Dim sourceSpan As Span(Of Integer) = sourceArray
' Create a Span<int> from the allocated unmanaged memory
Dim destinationSpan As New Span(Of Integer)(destinationPointer.ToPointer(), sourceArray.Length)
' Copy data from the source Span<int> to the destination Span<int>
sourceSpan.CopyTo(destinationSpan)
' Print the values in the destination memory
Console.WriteLine("Values in the destination memory:")
For Each value In destinationSpan
Console.Write($"{value} ")
Next value
Finally
' Deallocate the unmanaged memory when done
Marshal.FreeHGlobal(destinationPointer)
End Try
End Sub
End Class
在這個範例中
Marshal.AllocHGlobal為目標資料指派非託管記憶體。new Span<int>(destinationPointer.ToPointer(), sourceArray.Length)從已分配的非託管記憶體建立Span<int>。sourceSpan.CopyTo(destinationSpan)方法將資料從託管陣列複製到非託管記憶體。- 目的記憶體中的值會被列印出來,以驗證複製作業。
Marshal.FreeHGlobal(destinationPointer)方法用於完成後釋放未託管記憶體。
使用不安全的程式碼
在處理非管理記憶體時,也可能會使用指針的不安全程式碼。 在這種情況下,您可以使用 Unsafe.AsPointer() 方法從 Span 取得指針。
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
class Program
{
static void Main()
{
const int bufferSize = 100;
IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
// Create a Span from the unmanaged memory
Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
// Use unsafe code to work with pointers
unsafe
{
byte* pointer = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
// Use the pointer as needed...
}
// Don't forget to free the unmanaged memory when done
Marshal.FreeHGlobal(unmanagedMemory);
}
}
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
class Program
{
static void Main()
{
const int bufferSize = 100;
IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
// Create a Span from the unmanaged memory
Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
// Use unsafe code to work with pointers
unsafe
{
byte* pointer = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
// Use the pointer as needed...
}
// Don't forget to free the unmanaged memory when done
Marshal.FreeHGlobal(unmanagedMemory);
}
}
Imports System
Imports System.Runtime.InteropServices
Imports System.Runtime.CompilerServices
Friend Class Program
Shared Sub Main()
Const bufferSize As Integer = 100
Dim unmanagedMemory As IntPtr = Marshal.AllocHGlobal(bufferSize)
' Create a Span from the unmanaged memory
Dim span As New Span(Of Byte)(unmanagedMemory.ToPointer(), bufferSize)
' Use unsafe code to work with pointers
'INSTANT VB TODO TASK: C# 'unsafe' code is not converted by Instant VB:
' unsafe
' {
' byte* pointer = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
' ' Use the pointer as needed...
' }
' Don't forget to free the unmanaged memory when done
Marshal.FreeHGlobal(unmanagedMemory)
End Sub
End Class
在這個範例中,我們使用 Unsafe.AsPointer 方法從 Span 取得指標。 這可讓我們在直接處理指標時使用不安全的程式碼。
請記住,在使用非管理記憶體時,妥善管理分配和取消分配以避免記憶體洩漏是非常重要的。 始終使用適當的方法釋放非託管內存,例如 Marshal.FreeHGlobal()。 此外,使用不安全的程式碼時要小心,因為如果處理不當,可能會帶來潛在的安全風險。
Span 和異步方法呼叫。
在 C# 中將 Span 與異步方法呼叫結合使用是一個強大的組合,尤其是在處理大量資料或 I/O 作業時。 目標是在沒有不必要複製資料的情況下,有效率地處理非同步作業。 讓我們來探討如何在異步情境中利用 Span:
1.異步 I/O 操作: 2.
處理非同步 I/O 操作(例如向串流讀取或寫入資料)時,可以使用 Memory 或 Span 無需建立額外緩衝區即可有效處理資料。
using System;
using System.IO;
using System.Threading.Tasks;
class Program
{
static async Task ProcessDataAsync(Stream stream)
{
const int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
while (true)
{
int bytesRead = await stream.ReadAsync(buffer.AsMemory());
if (bytesRead == 0)
break;
// Process the data using Span without unnecessary copying
ProcessData(buffer.AsSpan(0, bytesRead));
}
}
static void ProcessData(Span<byte> data)
{
// Perform operations on the data
}
}
using System;
using System.IO;
using System.Threading.Tasks;
class Program
{
static async Task ProcessDataAsync(Stream stream)
{
const int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
while (true)
{
int bytesRead = await stream.ReadAsync(buffer.AsMemory());
if (bytesRead == 0)
break;
// Process the data using Span without unnecessary copying
ProcessData(buffer.AsSpan(0, bytesRead));
}
}
static void ProcessData(Span<byte> data)
{
// Perform operations on the data
}
}
Imports System
Imports System.IO
Imports System.Threading.Tasks
Friend Class Program
Private Shared Async Function ProcessDataAsync(ByVal stream As Stream) As Task
Const bufferSize As Integer = 4096
Dim buffer(bufferSize - 1) As Byte
Do
Dim bytesRead As Integer = Await stream.ReadAsync(buffer.AsMemory())
If bytesRead = 0 Then
Exit Do
End If
' Process the data using Span without unnecessary copying
ProcessData(buffer.AsSpan(0, bytesRead))
Loop
End Function
Private Shared Sub ProcessData(ByVal data As Span(Of Byte))
' Perform operations on the data
End Sub
End Class
在這個範例中,ReadAsync 方法以異步方式將資料從串流讀取到緩衝區中。 之後,ProcessData 方法會直接處理 Span
2. 異步檔案作業: 3.
與 I/O 作業類似,在處理異步檔案作業時,您可以使用 Span 來有效率地處理資料,而無需額外複製。
using System;
using System.IO;
using System.Threading.Tasks;
class Program
{
static async Task ProcessFileAsync(string filePath)
{
const int bufferSize = 4096;
using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[bufferSize];
while (true)
{
int bytesRead = await fileStream.ReadAsync(buffer.AsMemory());
if (bytesRead == 0)
break;
// Process the data using Span without unnecessary copying
ProcessData(buffer.AsSpan(0, bytesRead));
}
}
}
static void ProcessData(Span<byte> data)
{
// Perform operations on the data
}
}
using System;
using System.IO;
using System.Threading.Tasks;
class Program
{
static async Task ProcessFileAsync(string filePath)
{
const int bufferSize = 4096;
using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[bufferSize];
while (true)
{
int bytesRead = await fileStream.ReadAsync(buffer.AsMemory());
if (bytesRead == 0)
break;
// Process the data using Span without unnecessary copying
ProcessData(buffer.AsSpan(0, bytesRead));
}
}
}
static void ProcessData(Span<byte> data)
{
// Perform operations on the data
}
}
Imports System
Imports System.IO
Imports System.Threading.Tasks
Friend Class Program
Private Shared Async Function ProcessFileAsync(ByVal filePath As String) As Task
Const bufferSize As Integer = 4096
Using fileStream As New FileStream(filePath, FileMode.Open, FileAccess.Read)
Dim buffer(bufferSize - 1) As Byte
Do
Dim bytesRead As Integer = Await fileStream.ReadAsync(buffer.AsMemory())
If bytesRead = 0 Then
Exit Do
End If
' Process the data using Span without unnecessary copying
ProcessData(buffer.AsSpan(0, bytesRead))
Loop
End Using
End Function
Private Shared Sub ProcessData(ByVal data As Span(Of Byte))
' Perform operations on the data
End Sub
End Class
在這裡,ReadAsync 方法將資料從檔案串流讀入緩衝區,而 ProcessData 方法則直接從 Span
3. 異步任務處理: 4.
在處理產生或使用資料的非同步任務時,可以使用 Memory 或 Span 避免不必要的複製。
using System;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static async Task<int> ProcessDataAsync(int[] data)
{
// Asynchronous processing of data
await Task.Delay(1000);
// Returning the length of the processed data
return data.Length;
}
static async Task Main()
{
int[] inputData = Enumerable.Range(1, 1000).ToArray();
// Process the data asynchronously without copying
int processedLength = await ProcessDataAsync(inputData.AsMemory());
Console.WriteLine($"Processed data length: {processedLength}");
}
}
using System;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static async Task<int> ProcessDataAsync(int[] data)
{
// Asynchronous processing of data
await Task.Delay(1000);
// Returning the length of the processed data
return data.Length;
}
static async Task Main()
{
int[] inputData = Enumerable.Range(1, 1000).ToArray();
// Process the data asynchronously without copying
int processedLength = await ProcessDataAsync(inputData.AsMemory());
Console.WriteLine($"Processed data length: {processedLength}");
}
}
Imports System
Imports System.Linq
Imports System.Threading.Tasks
Friend Class Program
Private Shared Async Function ProcessDataAsync(ByVal data() As Integer) As Task(Of Integer)
' Asynchronous processing of data
Await Task.Delay(1000)
' Returning the length of the processed data
Return data.Length
End Function
Shared Async Function Main() As Task
Dim inputData() As Integer = Enumerable.Range(1, 1000).ToArray()
' Process the data asynchronously without copying
Dim processedLength As Integer = Await ProcessDataAsync(inputData.AsMemory())
Console.WriteLine($"Processed data length: {processedLength}")
End Function
End Class
在這個範例中,ProcessDataAsync 方法以異步方式處理資料,並返回已處理資料的長度,而不需要額外的副本。
介紹 IronPDF。
IronPDF 函式庫概述是 Iron Software 最新推出的 C# PDF 函式庫,可用於使用 C# 程式碼動態地即時產生精美的 PDF 文件。 IronPDF 提供多種功能,例如從 HTML 產生 PDF、將 HTML 內容轉換為 PDF、合併或分割 PDF 檔案等。
IronPDF 的主要功能是其HTML 轉 PDF 功能,可保留版面和樣式。 它可以從網頁內容產生 PDF,非常適合報告、發票和文件。 此工具支援將 HTML 檔案、URL 和 HTML 字串轉換為 PDF 檔案。
using IronPdf;
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
// 1. Convert HTML String to PDF
var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");
// 2. Convert HTML File to PDF
var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");
// 3. Convert URL to PDF
var url = "http://ironpdf.com"; // Specify the URL
var pdfFromUrl = renderer.RenderUrlAsPdf(url);
pdfFromUrl.SaveAs("URLToPDF.pdf");
}
}
using IronPdf;
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
// 1. Convert HTML String to PDF
var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");
// 2. Convert HTML File to PDF
var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");
// 3. Convert URL to PDF
var url = "http://ironpdf.com"; // Specify the URL
var pdfFromUrl = renderer.RenderUrlAsPdf(url);
pdfFromUrl.SaveAs("URLToPDF.pdf");
}
}
Imports IronPdf
Friend Class Program
Shared Sub Main(ByVal args() As String)
Dim renderer = New ChromePdfRenderer()
' 1. Convert HTML String to PDF
Dim htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>"
Dim pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent)
pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf")
' 2. Convert HTML File to PDF
Dim htmlFilePath = "path_to_your_html_file.html" ' Specify the path to your HTML file
Dim pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath)
pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf")
' 3. Convert URL to PDF
Dim url = "http://ironpdf.com" ' Specify the URL
Dim pdfFromUrl = renderer.RenderUrlAsPdf(url)
pdfFromUrl.SaveAs("URLToPDF.pdf")
End Sub
End Class
安裝
IronPDF 可使用 NuGet package manager for IronPDF 控制台或 Visual Studio package manager 進行安裝。
dotnet add package IronPdf // Or Install-Package IronPdf
。
using System;
using IronPdf;
class Program
{
static void Main()
{
Console.WriteLine("Generating PDF using IronPDF.");
var displayFirstName = "<p>First Name is Joe</p>".AsSpan();
var displayLastName = "<p>Last Name is Doe</p>".AsSpan();
var displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan();
var start = "<html><body>".AsSpan();
var end = "</body></html>".AsSpan();
var content = string.Concat(start.ToString(), displayFirstName.ToString(), displayLastName.ToString(), displayAddress.ToString(), end.ToString());
var pdfDocument = new ChromePdfRenderer();
pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf");
}
}
using System;
using IronPdf;
class Program
{
static void Main()
{
Console.WriteLine("Generating PDF using IronPDF.");
var displayFirstName = "<p>First Name is Joe</p>".AsSpan();
var displayLastName = "<p>Last Name is Doe</p>".AsSpan();
var displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan();
var start = "<html><body>".AsSpan();
var end = "</body></html>".AsSpan();
var content = string.Concat(start.ToString(), displayFirstName.ToString(), displayLastName.ToString(), displayAddress.ToString(), end.ToString());
var pdfDocument = new ChromePdfRenderer();
pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf");
}
}
Imports System
Imports IronPdf
Friend Class Program
Shared Sub Main()
Console.WriteLine("Generating PDF using IronPDF.")
Dim displayFirstName = "<p>First Name is Joe</p>".AsSpan()
Dim displayLastName = "<p>Last Name is Doe</p>".AsSpan()
Dim displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan()
Dim start = "<html><body>".AsSpan()
Dim [end] = "</body></html>".AsSpan()
Dim content = String.Concat(start.ToString(), displayFirstName.ToString(), displayLastName.ToString(), displayAddress.ToString(), [end].ToString())
Dim pdfDocument = New ChromePdfRenderer()
pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf")
End Sub
End Class
在這個範例中,我們使用 Span 與 IronPDF 來產生 PDF 文件。
輸出:
!a href="/static-assets/pdf/blog/csharp-span/csharp-span-2.webp">C# Span (How It Works For Developers):圖 2 - 控制台輸出
生成的 PDF:

授權(可免費試用)
IronPDF授權資訊。 此 key 需要放在 appsettings.json 中。
"IronPdf.LicenseKey": "your license key"
提供您的電子郵件以取得試用授權。
結論
C# 的 Span 提供了一種強大而高效的方式來處理記憶體,在效能和靈活性方面都具有優勢。 它的非擁有性與連續性特點,使其特別適合那些需要盡量減少記憶體分配和複製的場景。 透過利用 Span,開發人員可以在從字串操作到高效能數值處理等各種應用程式中取得更好的效能。 透過了解 Span 的特性並考量其局限性,開發人員可以安全且有效率地執行各種記憶體操作任務。 配合IronPDF函式庫概述,它可以用來產生超棒的 PDF 文件,沒有等待和產量的界限。
請造訪 IronPDF"快速入門文件"頁面。
常見問題
什麼是 C# 中的 Span,為什麼它很重要?
Span
Span 如何優化 C# 中的內存操作?
Span
Span 和 ReadOnlySpan 之間的區別是什麼?
Span
Span 可以用於 C# 中的非托管記憶體嗎?
是的,可以通過從指向非托管記憶體的指針創建 span 的方式,使 Span 用於非托管記憶體。這允許直接操作記憶體,同時確保其通過Marshal.AllocHGlobal 和 Marshal.FreeHGlobal 等方法正確分配和釋放。
IronPDF 如何與 Span 整合以生成 PDF?
IronPDF 可以與 Span
使用 Span 進行內存管理的限制是什麼?
使用 Span
如何在 C# 中安裝 IronPDF 以進行 PDF 操作?
可以使用 NuGet 套件管理器在 C# 項目中安裝 IronPDF。使用 dotnet add package IronPDF 或 Install-Package IronPDF 等命令將其添加到您的項目中。
使用 Span 進行字符串操作的好處是什麼?
Span
IronPDF 有試用版本嗎?
是的,IronPDF 提供一個試用授權,可以通過提供您的電子郵件來獲得。試用授權密鑰應放在 appsettings.json 文件中以使用該庫。
在非同步方法調用中可以使用 Span 嗎?
是的,可以在非同步方法調用中使用 Span 來有效地處理資料而無需不必要的複製。這在 I/O 操作和文件處理中特別有用,利用 Memory 或 Span。



