C# Span(對於開發者的運行原理)
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
ReadOnlySpan
雖然 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. 操作子字串
在 ReadOnlySpanSlice。
// 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. 在字串中搜尋
ReadOnlySpanIndexOf()。
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
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 執行時的垃圾收集器管理的記憶體,通常涉及使用原生記憶體分配和釋放。 以下是 Span 在 C# 中與非管理記憶體一起運作的方式。
分配非管理記憶體
要分配非管理記憶體,您可以使用 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
在上述程式碼中,我們使用 Marshal.AllocHGlobal 分配了一個不受管理的記憶體塊,然後使用從 unmanaged memory 獲得的指標建立了一個 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
在這個範例中:
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 操作:
在處理非同步 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. 非同步檔案操作:
與 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. 非同步任務處理:
在與產生或消耗資料的非同步任務配合時,您可以使用 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 程式庫,可用於即時動態生成精美的 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 程式包管理器 主控台或使用 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
在這個範例中,我們將 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操作?
IronPDF可以通過使用NuGet套件管理器安裝到C#專案中。使用dotnet add package IronPdf或Install-Package IronPdf等命令即可將其新增到項目中。
使用Span進行字串操作的好處是什麼?
Span通過最小化記憶體分配和複製來實現高效的字串操作。這在需要處理大量字串資料的性能關鍵程式碼中尤為有利。
IronPDF有試用版本嗎?
是的,IronPDF提供試用授權,您可以通過提供電子郵件來獲得。試用授權金鑰應放置在appsettings.json文件中以便使用程式庫。
Span可以用於非同步方法調用嗎?
是的,Span可以用於非同步方法調用,以高效處理資料而不需不必要的複製。這在I/O操作和文件處理中特別有用,利用Memory或Span。




