.NET幫助 C# Span(對於開發者的運行原理) Jacob Mellor 更新:2025年6月22日 下載 IronPDF NuGet 下載 DLL 下載 Windows 安裝程式 開始免費試用 LLM副本 LLM副本 將頁面複製為 Markdown 格式,用於 LLMs 在 ChatGPT 中打開 請向 ChatGPT 諮詢此頁面 在雙子座打開 請向 Gemini 詢問此頁面 在 Grok 中打開 向 Grok 詢問此頁面 打開困惑 向 Perplexity 詢問有關此頁面的信息 分享 在 Facebook 上分享 分享到 X(Twitter) 在 LinkedIn 上分享 複製連結 電子郵件文章 Span 是在 C# 7.2 中引入的一種類型,作為 System 命名空間中 Span 結構的一部分。 它旨在表示一個連續的任意記憶體區域。 與管理堆等集合不同,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 $vbLabelText $csharpLabel ReadOnlySpan 雖然 Span 可變並允許對底層數據進行修改,但 ReadOnlySpan 是記憶體的不可變檢視。 它為連續的記憶體區域提供只讀界面,適合只需要讀取而不需要修改的場景。 以下是一些要點。 1. 只讀檢視 正如名稱所示,ReadOnlySpan 允許建立一個只讀的記憶體塊檢視。 這意味著通過 ReadOnlySpan 無法修改元素。 2. 記憶體表示 像 Span 一樣,ReadOnlySpan 並不擁有它指向的記憶體。 它引用現有記憶體,可指向數組、堆疊分配的記憶體或本機記憶體。 3. 性能優勢 與傳統集合類型相比,特別是在處理大量數據時,ReadOnlySpan 像 Span 一樣,可提供更好的性能,因為它減少了拷貝的需要。 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 $vbLabelText $csharpLabel 創建 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 $vbLabelText $csharpLabel 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' $vbLabelText $csharpLabel 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)) $vbLabelText $csharpLabel 4. 搜索字符串中的內容 用 ReadOnlySpan 來在字符串中使用 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 $vbLabelText $csharpLabel 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 $vbLabelText $csharpLabel 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 $vbLabelText $csharpLabel 7. 將子字符串傳遞給 API 在使用操作字符 Span 的外部程式庫或 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)) $vbLabelText $csharpLabel ReadOnlySpan 提供了一種更高效處理字符串的方式,特別是在需要最小化內存分配和拷貝的情境下。 這是一個用於優化性能關鍵代碼的強大工具,特別是在處理大量字符串數據時大有裨益。 Span 的限制 雖然 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 表示的非連續記憶體。 適應此類情況可能需要額外的中間步驟或轉換以確保兼容性。 Span 和非管理記憶體 在 C# 中,Span 可以有效利用非管理記憶體來以受控和高效的方式執行記憶體相關操作。 非管理記憶體是指不由 .NET 運行時的垃圾收集器管理的記憶體,通常涉及使用本地記憶體的分配和解除分配。 以下是在 C# 中如何將 Span 與非管理記憶體結合使用。 分配非管理記憶體 要分配非管理記憶體,您可以使用 System.Runtime.InteropServices.MemoryMarshal 類。 Marshal.AllocHGlobal 方法分配記憶體並返回分配塊的指針。 所分配的記憶體或記憶體地址由 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); } } 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 $vbLabelText $csharpLabel 在上述源代碼中,我們使用 Marshal.AllocHGlobal 分配了一個非管理記憶體塊,然後使用從非管理記憶體獲得的指針創建了一個 Span<byte>。 這允許我們使用熟悉的 Span API 來處理非管理記憶體。 需要注意的是,在處理非管理記憶體時,您有責任管理記憶體的分配和解除分配。 將數據拷貝至及從非管理記憶體中 Span 提供了如 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 $vbLabelText $csharpLabel 在本範例中: 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 $vbLabelText $csharpLabel 在本例中,我們使用 Unsafe.AsPointer 方法從 Span 獲得指針。 這允許我們在直接處理指針時使用不安全代碼。 請記住,在處理非管理記憶體時,正確管理內存的分配和釋放至關重要,以避免記憶體洩露。 始終使用適當的方法釋放非管理記憶體,例如 Marshal.FreeHGlobal()。 另外,使用不安全代碼時要謹慎,因為如果處理不當,它可能引入潛在的安全風險。 Span 與異步方法調用 在 C# 中,將 Span 與異步方法調用結合使用是一種強大的組合,特別是在處理大量數據或 I/O 操作時。 目標是高效處理異步操作,而不需不必要的數據拷貝。 我們來探討如何在異步場景中利用 Span: 1. 異步 I/O 操作: 在處理異步 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 $vbLabelText $csharpLabel 在本例中,ReadAsync 方法將數據異步讀取至緩衝區中。 然後 ProcessData 方法直接從 Span 中處理數據,而不將其拷貝到另一個緩衝區。 2. 異步文件操作: 類似於 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 $vbLabelText $csharpLabel 在此,ReadAsync 方法將數據從文件流讀入緩衝區中,而 ProcessData 方法直接從 Span 處理數據。 3. 異步任務處理: 在處理生產數據或消費數據的異步任務時,可以使用 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 $vbLabelText $csharpLabel 在本例中,ProcessDataAsync 方法異步處理數據,並返回處理過的數據長度,而不需要額外的拷貝。 介紹IronPDF IronPDF 程式庫概述 是來自 Iron Software 的最新 C# PDF 程式庫,可以動態生成優美的 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 $vbLabelText $csharpLabel 安裝 IronPDF 可以通過使用 IronPDF 的 NuGet 套件管理器控制台或使用 Visual Studio 套件管理器安裝。 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 $vbLabelText $csharpLabel 在本例中,我們使用 Span 配合 IronPDF 生成一個 PDF 文件。 輸出: 生成的 PDF: 授權 (提供免費試用) IronPDF 授權資訊。 此密鑰需要放置在appsettings.json中。 "IronPdf.LicenseKey": "your license key" 提供您的電子郵件以獲取試用授權。 結論 C# 中的 Span 提供了一種強大且高效的記憶體操作方式,雖然在性能和靈活性方面有很多優勢。 它的無擁有、連續特性使其特別適合於最小化內存分配和拷貝的重要場景。 通過利用 Span,開發者可以在多種應用中實現更好的性能,從字符串操作到高效能數值處理。 通過理解其特性並考慮其限制,開發者可以安全且高效地利用 Span 替各種記憶體操控任務。 與 IronPDF 程式庫概述 一同使用時,它可以在無需 await 和 yield 的邊境下生成優秀的 PDF 文件。 請訪問 IronPDF 的快速開始文件頁面。 常見問題解答 什麼是 C# 中的 Span,為什麼它很重要? Span 是在 C# 7.2 中引入的一種類型,表示一個連續的內存區域。它之所以重要是因為它允許開發者高效地執行低級內存操作,而不需要堆分配的開銷,並保持 C# 的安全性和性能。 Span 如何優化 C# 中的內存操作? Span 通過提供對內存的零複製抽象,優化內存操作,允許開發者引用現有的內存區塊而不需要複製數據。這會帶來性能的提升,特別是在處理大量數據的應用中。 Span 和 ReadOnlySpan 之間的區別是什麼? Span 是一個可變的內存視圖,允許進行修改,而 ReadOnlySpan 則提供只讀視圖。當數據不應被修改時,使用 ReadOnlySpan,它在確保數據完整性的同時提供類似的性能優勢。 Span 可以用於 C# 中的非托管記憶體嗎? 是的,可以通過從指向非托管記憶體的指針創建 span 的方式,使 Span 用於非托管記憶體。這允許直接操作記憶體,同時確保其通過Marshal.AllocHGlobal 和 Marshal.FreeHGlobal 等方法正確分配和釋放。 IronPDF 如何與 Span 整合以生成 PDF? IronPDF 可以與 Span 一起工作,以動態生成 PDF,通過有效管理內存並避免不必要的分配來提高性能。這種整合使得開發者能夠從網頁內容創建 PDF 文檔,性能有所提升。 使用 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。 Jacob Mellor 立即與工程團隊聊天 首席技術官 Jacob Mellor是Iron Software的首席技術官,也是開創C# PDF技術的前瞻性工程師。作為Iron Software核心代碼庫的原始開發者,他自公司成立以來就塑造了公司的產品架構,並與CEO Cameron Rimington將公司轉型為服務NASA、Tesla以及全球政府機構的50多人公司。Jacob擁有曼徹斯特大學土木工程一級榮譽學士學位(1998年–2001年)。他於1999年在倫敦開立首家軟體公司,並於2005年建立了他的第一個.NET組件,專注於解決Microsoft生態系統中的複雜問題。他的旗艦作品IronPDF和Iron Suite .NET程式庫全球已獲得超過3000萬次NuGet安裝,他的基礎代碼不斷在全球各地驅動開發者工具。擁有25年以上的商業經驗和41年的編碼專業知識,Jacob仍然專注於推動企業級C#、Java和Python PDF技術的創新,同時指導下一代技術領導者。 相關文章 更新2026年2月20日 銜接 CLI 簡化與 .NET : 使用 Curl DotNet 與 IronPDF for .NET Jacob Mellor 藉由 CurlDotNet 彌補了這方面的不足,CurlDotNet 是為了讓 .NET 生態系統能熟悉 cURL 而建立的函式庫。 閱讀更多 更新2025年12月20日 RandomNumberGenerator C# 使用RandomNumberGenerator C#類可以幫助將您的PDF生成和編輯項目提升至新水準 閱讀更多 更新2025年12月20日 C#字符串等於(它如何對開發者起作用) 當結合使用強大的PDF庫IronPDF時,開關模式匹配可以讓您構建更智能、更清晰的邏輯來進行文檔處理 閱讀更多 C# IDE(對於開發者的運行原理)Opentelemetry C#(對於開發者...
更新2026年2月20日 銜接 CLI 簡化與 .NET : 使用 Curl DotNet 與 IronPDF for .NET Jacob Mellor 藉由 CurlDotNet 彌補了這方面的不足,CurlDotNet 是為了讓 .NET 生態系統能熟悉 cURL 而建立的函式庫。 閱讀更多