.NET幫助 C# Span(對於開發者的運行原理) Curtis Chau 更新日期:6月 22, 2025 Download IronPDF NuGet 下載 DLL 下載 Windows 安裝程式 Start Free Trial Copy for LLMs Copy for LLMs Copy page as Markdown for LLMs Open in ChatGPT Ask ChatGPT about this page Open in Gemini Ask Gemini about this page Open in Grok Ask Grok about this page Open in Perplexity Ask Perplexity about this page Share Share on Facebook Share on X (Twitter) Share on LinkedIn Copy URL Email article Span 是 C# 7.2 中引入的類型,作為 System 命名空間中的 Span 結構的一部分。 它旨在表示任意內存的連續區域。 與托管堆等數組或集合不同,Span 不擁有其指向的堆棧內存或內存區域; 相反,它提供了對現有內存塊的輕量級視圖。 這種特性使得 Span 對於需要高效處理內存緩衝區而不增加額外開銷和不安全代碼情況的場景特別強大。 Later in this article, we shall also see the introduction to the IronPDF library from Iron Software. 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 當處理操作字元範圍的外部函式庫或 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 提供了諸如 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 $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 library overview is the latest C# PDF library from 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 可以通過 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。 Curtis Chau 立即與工程團隊聊天 技術作家 Curtis Chau 擁有卡爾頓大學計算機科學學士學位,專注於前端開發,擅長於 Node.js、TypeScript、JavaScript 和 React。Curtis 熱衷於創建直觀且美觀的用戶界面,喜歡使用現代框架並打造結構良好、視覺吸引人的手冊。除了開發之外,Curtis 對物聯網 (IoT) 有著濃厚的興趣,探索將硬體和軟體結合的創新方式。在閒暇時間,他喜愛遊戲並構建 Discord 機器人,結合科技與創意的樂趣。 相關文章 更新日期 9月 4, 2025 RandomNumberGenerator C# 使用RandomNumberGenerator C#類可以幫助將您的PDF生成和編輯項目提升至新水準 閱讀更多 更新日期 9月 4, 2025 C#字符串等於(它如何對開發者起作用) 當結合使用強大的PDF庫IronPDF時,開關模式匹配可以讓您構建更智能、更清晰的邏輯來進行文檔處理 閱讀更多 更新日期 8月 5, 2025 C#開關模式匹配(對開發者來說是如何工作的) 當結合使用強大的PDF庫IronPDF時,開關模式匹配可以讓您構建更智能、更清晰的邏輯來進行文檔處理 閱讀更多 C# IDE(對於開發者的運行原理)Opentelemetry C#(對於開發者...