.NET 幫助

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
{
    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
{
    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
	Private 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

唯讀範圍

雖然 Span是可變的並允許對底層數據進行修改,但 ReadOnlySpan是記憶體的不可變視圖。 它提供了一個唯讀介面給一個連續的記憶體區域,適合於只需要讀取資料而不進行修改的情境。

以下是一些要點。

唯讀檢視

顧名思義,ReadOnlySpan允許您建立記憶體區塊的唯讀視圖。 這意味著您無法透過 ReadOnlySpan修改元素。

2. 記憶體表示

與 Span一樣,ReadOnlySpan不擁有其指向的記憶體。 它指的是現有的記憶體,並且可以指向陣列、堆疊分配的記憶體或本機記憶體。

3. 性能優勢

如同 Span,ReadOnlySpan可以比傳統的集合類型提供更好的效能,尤其是處理大量數據時,因為它減少了複製的需求。

4. 無邊界檢查

與 Span一樣,ReadOnlySpan不執行邊界檢查。 開發人員有責任確保操作保持在基礎記憶體的範圍內。

5. 與陣列切片的使用

ReadOnlySpan支援切片,允許您建立引用原始記憶體部分的子 span。

範例

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

ReadOnlySpan<char> substringSpan = spanFromString.Slice(startIndex, length);
ReadOnlySpan<char> substringSpan = spanFromString.Slice(startIndex, length);
Dim substringSpan As ReadOnlySpan(Of Char) = spanFromString.Slice(startIndex, length)
$vbLabelText   $csharpLabel

3. 傳遞子字串到方法

將 ReadOnlySpan作為參數傳遞給該方法。

void ProcessSubstringfromReadOnlySpan(ReadOnlySpan<char> substring)
{
    // Perform operations on the substring
}
// Usage
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(startIndex, length));
void ProcessSubstringfromReadOnlySpan(ReadOnlySpan<char> substring)
{
    // Perform operations on the substring
}
// Usage
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(startIndex, length));
Private Sub ProcessSubstringfromReadOnlySpan(ByVal substring As ReadOnlySpan(Of Char))
	' Perform operations on the substring
End Sub
' Usage
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(startIndex, length))
$vbLabelText   $csharpLabel

4. 字串內搜尋

使用 IndexOf() 在字串中搜尋 ReadOnlySpan

int index = stringSpan.IndexOf('W');
int index = stringSpan.IndexOf('W');
Dim index As Integer = stringSpan.IndexOf("W"c)
$vbLabelText   $csharpLabel

5. 使用記憶體映射檔案

ReadOnlySpan配合記憶體映射文件可以提高效率。

using (var memmf = MemoryMappedFile.CreateFromFile("data.bin"))
{
    using (var accessor = memmf.CreateViewAccessor())
    {
        ReadOnlySpan<byte> dataSpan;
        accessor.Read(0, out dataSpan);
        // Process data directly from the memory-mapped file
        ProcessData(dataSpan);
    }
}
using (var memmf = MemoryMappedFile.CreateFromFile("data.bin"))
{
    using (var accessor = memmf.CreateViewAccessor())
    {
        ReadOnlySpan<byte> dataSpan;
        accessor.Read(0, out dataSpan);
        // Process data directly from the memory-mapped file
        ProcessData(dataSpan);
    }
}
Using memmf = MemoryMappedFile.CreateFromFile("data.bin")
	Using accessor = memmf.CreateViewAccessor()
		Dim dataSpan As ReadOnlySpan(Of Byte) = Nothing
		accessor.Read(0, dataSpan)
		' Process data directly from the memory-mapped file
		ProcessData(dataSpan)
	End Using
End Using
$vbLabelText   $csharpLabel

6. 高效字串操作

ReadOnlySpan可用於高效的字串操作。

// Replace a character in a substring without creating a new string
spanFromString.Slice(startIndex, length).CopyTo(newSpan);
// Replace a character in a substring without creating a new string
spanFromString.Slice(startIndex, length).CopyTo(newSpan);
' Replace a character in a substring without creating a new string
spanFromString.Slice(startIndex, length).CopyTo(newSpan)
$vbLabelText   $csharpLabel

7. 傳遞子字串到 APIs

使用外部庫或操作字元範圍的 API 時。

void ExternalApiMethod(ReadOnlySpan<char> data)
{
    // Call the external API with the character span
}
// Usage
ExternalApiMethod(spanFromString.Slice(startIndex, length));
void ExternalApiMethod(ReadOnlySpan<char> data)
{
    // Call the external API with the character span
}
// Usage
ExternalApiMethod(spanFromString.Slice(startIndex, length));
Private Sub ExternalApiMethod(ByVal data As ReadOnlySpan(Of Char))
	' Call the external API with the character span
End Sub
' Usage
ExternalApiMethod(spanFromString.Slice(startIndex, length))
$vbLabelText   $csharpLabel

ReadOnlySpan提供了一種更高效地處理字串的方法,特別是在應該將記憶體分配和複製最小化的情境中。 這是一個強大的工具,可以優化性能關鍵代碼,在處理大量字串數據時特別有益。

範圍限制

雖然 C# 中的 Span 是一個強大的功能,具有許多優勢,但在連續和非連續記憶體的情境下,確實存在某些限制和考量。 讓我們來探索這些限制:

連續記憶體緩衝區

1.1 無自動記憶體管理

Span不管理其指向的記憶體。 這意味著如果底層的受管理記憶體被釋放或超出範圍,使用 Span會導致未定義的行為或潛在的崩潰。 開發人員在使用 Span時需要確保底層記憶體仍然有效。

1.2 無垃圾回收

由於 Span<T> 不擁有記憶體,故不受垃圾回收影響。 因此,當您處理堆疊分配的記憶體或生命週期比 Span本身短的記憶體時,需要小心。

1.3 範圍檢查已停用

Span和 ReadOnlySpan默認不執行邊界檢查。 如果不小心使用,這可能會導致存取無效的記憶體位置。 開發人員需要手動確保對 Span的運作保持在底層記憶體的界限內。

1.4 不支援非連續記憶體

Span被設計用於與連續記憶體一起工作。 如果您有不連續的記憶體或需要表示更複雜的資料結構,Span可能不是最合適的選擇。

1.5 並非所有操作都受支持

雖然 Span支援許多常見的操作,如切片、索引和迭代,但並非所有操作都被支援。 例如,您無法調整 Span的大小,並且不允許進行涉及更改基礎記憶體長度的某些操作。

1.6 有限平台相容性

雖然Span是.NET Standard和.NET Core的一部分,但它可能不在所有平台或環境上可用。 如果您計劃在代碼中使用 Span,則確保您的目標平台支持它是至關重要的。

非連續記憶體緩衝區

2.1 對非連續記憶體的有限支持

ReadOnlySpan主要設計用來無縫配合連續的記憶體區塊或緩衝區使用。 當涉及非連續記憶體緩衝區或具有記憶體間隙的資料結構時,它可能不是最合適的選擇。

2.2 結構限制

某些依賴非連續記憶體的資料結構或情境可能無法與 ReadOnlySpan很好地對齊。 例如,像鏈接列表或圖結構這樣的數據結構可能不太適合,因為 ReadOnlySpan<T> 需要連續的記憶體。

2.3 複雜指標操作

在涉及非连续内存的情境中,尤其是那些需要复杂指针运算的情况,ReadOnlySpan\<T> 可能无法提供像 C++ 之類的语言中的原始指针相同的低层控制和灵活性。 在這種情況下,使用指針的不安全代碼可能更為合適。

2.4 某些 API 缺乏直接支援

類似於連續記憶體,值得注意的是,並非所有的 API 或函式庫都可能直接支援由 ReadOnlySpan表示的非連續記憶體。 適應這些情況可能需要額外的中間步驟或轉換以確保相容性。

跨度和非托管記憶體

在 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。 這使我們可以使用熟悉的 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 = MemoryMarshal.Allocate<int>(sourceArray.Length);
        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 = MemoryMarshal.Cast<int, byte>(destinationPointer, 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
            MemoryMarshal.Free(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 = MemoryMarshal.Allocate<int>(sourceArray.Length);
        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 = MemoryMarshal.Cast<int, byte>(destinationPointer, 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
            MemoryMarshal.Free(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 = MemoryMarshal.Allocate(Of Integer)(sourceArray.Length)
		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 Span(Of Integer) = MemoryMarshal.Cast(Of Integer, Byte)(destinationPointer, 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
			MemoryMarshal.Free(destinationPointer)
		End Try
	End Sub
End Class
$vbLabelText   $csharpLabel

在此範例中:

MemoryMarshal.Allocate(sourceArray.Length) 為目標數據分配非受控記憶體。 MemoryMarshal.Cast<int, byte>(destinationPointer, sourceArray.Length) 從分配的非托管記憶體中創建一個 Span。 sourceSpan.CopyTo(destinationSpan) 方法將資料從受控陣列複製到非受控記憶體。 為了驗證複製操作,將目的地記憶體中的值列印出來。 MemoryMarshal.Free(destinationPointer) 方法在完成後用於釋放非托管內存。

使用不安全程式碼

在處理非受控記憶體時,您也可以使用帶指標的非安全代碼。 在這種情況下,您可以使用 Unsafe.AsPointer() 方法從 Span 獲取指標。

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 unsafe code to work with pointers
        // ref t
        unsafe
        {
            byte* pointer = (byte*)Unsafe.AsPointer(ref struct 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;
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
        // ref t
        unsafe
        {
            byte* pointer = (byte*)Unsafe.AsPointer(ref struct 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
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
		' ref t
'INSTANT VB TODO TASK: C# 'unsafe' code is not converted by Instant VB:
'		unsafe
'		{
'			byte* pointer = (byte*)Unsafe.AsPointer(ref struct 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())釋放非受控記憶體。 此外,使用不安全的代碼時需謹慎,因為如果處理不當,可能會帶來潛在的安全風險。

跨度和非同步方法呼叫

在C#中,將Span與非同步方法調用結合使用是一種強大的組合,尤其是在處理大量數據或I/O操作時。 目標是有效處理異步操作,避免不必要的數據複製。 讓我們探討如何在異步場景中利用 Span:

非同步 I/O 操作:

處理異步 I/O 操作時,例如讀取或寫入資料到流中,您可以使用 Memory或 Span來有效地處理數據,而不需要創建額外的緩衝區。

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));
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
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));
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
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 Sub ProcessData(ByVal data As Span(Of Byte))
	' Perform operations on the data
End Sub
$vbLabelText   $csharpLabel

在此示例中,ReadAsync 方法將資料以非同步的方式從串流讀入緩衝區。 接下來,ProcessData 方法直接從 Span處理數據,而不需複製到其他緩衝區。

2. 非同步檔案操作:

類似於 I/O 操作,處理非同步文件操作時可以使用 Span 來高效處理數據,而不需額外複製。

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));
        }
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
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));
        }
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
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 Sub ProcessData(ByVal data As Span(Of Byte))
	' Perform operations on the data
End Sub
$vbLabelText   $csharpLabel

在這裡,ReadAsync 方法從文件流中讀取數據到緩衝區,而 ProcessData 方法直接從 Span中處理數據。

3. 非同步任務處理:

當處理產生或消耗資料的非同步任務時,您可以使用 Memory或 Span來避免不必要的複製。

async Task<int> ProcessDataAsync(int[] data)
{
    // Asynchronous processing of data
    await Task.Delay(1000);
    // Returning the length of the processed data
    return data.Length;
}
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}");
}
async Task<int> ProcessDataAsync(int[] data)
{
    // Asynchronous processing of data
    await Task.Delay(1000);
    // Returning the length of the processed data
    return data.Length;
}
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}");
}
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
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
$vbLabelText   $csharpLabel

在此範例中,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
$vbLabelText   $csharpLabel

安裝

IronPDF 可以使用 NuGet 套件管理器 控制台或使用 Visual Studio 套件管理器 安裝。

dotnet add package IronPdf
// Or
Install-Package IronPdf
dotnet add package IronPdf
// Or
Install-Package IronPdf
'INSTANT VB TODO TASK: The following line uses invalid syntax:
'dotnet add package IronPdf Install-Package IronPdf
$vbLabelText   $csharpLabel

C# Span (它如何對開發人員運作): 圖1 - 使用NuGet包管理器安裝IronPDF,通過在NuGet包管理器的搜索欄中搜尋ironpdf

using System;
class Program
{
    static void Main()
    {
        Console.WriteLine("Generating PDF using IronPDF.");
        var displayFirstName = "<p>First Name is Joe</p>".AsSpan();
        var displayLastName = "<p>First Name is Doe</p>".AsSpan();
        var displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan();
        var start = @"<!DOCTYPE html>
<html>
<body>".AsSpan();
        var end = @"<!DOCTYPE html>
<html>
<body>";
        var content = string.Concat(start, displayFirstName, displayLastName, string.Concat(displayAddress, end));
        var pdfDocument = new ChromePdfRenderer();
        pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf");
    }
}
using System;
class Program
{
    static void Main()
    {
        Console.WriteLine("Generating PDF using IronPDF.");
        var displayFirstName = "<p>First Name is Joe</p>".AsSpan();
        var displayLastName = "<p>First Name is Doe</p>".AsSpan();
        var displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan();
        var start = @"<!DOCTYPE html>
<html>
<body>".AsSpan();
        var end = @"<!DOCTYPE html>
<html>
<body>";
        var content = string.Concat(start, displayFirstName, displayLastName, string.Concat(displayAddress, end));
        var pdfDocument = new ChromePdfRenderer();
        pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf");
    }
}
Imports System
Friend Class Program
	Shared Sub Main()
		Console.WriteLine("Generating PDF using IronPDF.")
		Dim displayFirstName = "<p>First Name is Joe</p>".AsSpan()
		Dim displayLastName = "<p>First Name is Doe</p>".AsSpan()
		Dim displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan()
		Dim start = "<!DOCTYPE html>
<html>
<body>".AsSpan()
		Dim [end] = "<!DOCTYPE html>
<html>
<body>"
		Dim content = String.Concat(start, displayFirstName, displayLastName, String.Concat(displayAddress, [end]))
		Dim pdfDocument = New ChromePdfRenderer()
		pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf")
	End Sub
End Class
$vbLabelText   $csharpLabel

在此範例中,我們使用SpanIronPDF來生成 PDF 文件。

輸出:

C# Span(開發人員如何使用):圖2 - 控制台輸出

生成的 PDF:

C# Span(如何為開發人員工作):圖 3 - PDF 輸出

授權(免費試用可用)

IronPDF 授權資訊。 此鍵需要放置在 appsettings.json 中。

"IronPdf.LicenseKey": "your license key"
"IronPdf.LicenseKey": "your license key"
'INSTANT VB TODO TASK: The following line uses invalid syntax:
'"IronPdf.LicenseKey": "your license key"
$vbLabelText   $csharpLabel

提供您的電子郵件以獲取試用許可證。

結論

在 C# 中,Span提供了一種強大且有效率的方式來處理記憶體,為性能和靈活性方面帶來了好處。 其非擁有且連續的特性,特別適合在需要最大限度減少記憶體分配和複製的情境下使用。 通過利用 Span,開發人員可以在從字串操作到高效能數字處理的各類應用中實現更好的效能。 透過了解其功能並考慮其限制,開發者可以安全且高效地利用 Span進行各種內存操作任務。 除了IronPDF 函式庫概述之外,它還可以用來生成無需 await 和 yield 邊界的出色 PDF 文件。

請訪問IronPDF 的快速入門文檔頁面

Chipego
奇佩戈·卡林达
軟體工程師
Chipego 擁有天生的傾聽技能,這幫助他理解客戶問題,並提供智能解決方案。他在獲得信息技術理學學士學位後,于 2023 年加入 Iron Software 團隊。IronPDF 和 IronOCR 是 Chipego 專注的兩個產品,但隨著他每天找到新的方法來支持客戶,他對所有產品的了解也在不斷增長。他喜歡在 Iron Software 的協作生活,公司內的團隊成員從各自不同的經歷中共同努力,創造出有效的創新解決方案。當 Chipego 離開辦公桌時,他常常享受讀好書或踢足球的樂趣。
< 上一頁
C# IDE(開發人員如何使用它)
下一個 >
Opentelemetry C#(開發人員如何使用)