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
这里有几个要点。
1. 只读视图
顾名思义,ReadOnlySpan
2. 内存表示
与 Span
3. 性能优势
与 Span
4. 无边界检查
和 Span
5. 与数组切片的使用
ReadOnlySpan
示例
using System;
class Program
{
static void Main()
{
int[] array = { 1, 2, 3, 4, 5 };
// Create a read-only span that points to the entire array
ReadOnlySpan<int> readOnlySpan = array;
// Access and print the data through the read-only span
foreach (var item in readOnlySpan)
{
Console.WriteLine(item);
}
// Note: The following line would result in a compilation error since readOnlySpan is read-only.
// readOnlySpan[2] = 10;
}
}
using System;
class Program
{
static void Main()
{
int[] array = { 1, 2, 3, 4, 5 };
// Create a read-only span that points to the entire array
ReadOnlySpan<int> readOnlySpan = array;
// Access and print the data through the read-only span
foreach (var item in readOnlySpan)
{
Console.WriteLine(item);
}
// Note: The following line would result in a compilation error since readOnlySpan is read-only.
// readOnlySpan[2] = 10;
}
}
Imports System
Friend Class Program
Shared Sub Main()
Dim array() As Integer = { 1, 2, 3, 4, 5 }
' Create a read-only span that points to the entire array
Dim readOnlySpan As ReadOnlySpan(Of Integer) = array
' Access and print the data through the read-only span
For Each item In readOnlySpan
Console.WriteLine(item)
Next item
' Note: The following line would result in a compilation error since readOnlySpan is read-only.
' readOnlySpan[2] = 10;
End Sub
End Class
有多种不同方法可以创建和使用 ReadOnlySpan。 以下是一些示例。
1. 从字符串创建 ReadOnlySpan
string msg = "Hello, World!";
ReadOnlySpan<char> span1 = msg.AsSpan();
// Read-only manipulation
char firstChar = span1[0];
Console.WriteLine(firstChar); // Outputs: H
string msg = "Hello, World!";
ReadOnlySpan<char> span1 = msg.AsSpan();
// Read-only manipulation
char firstChar = span1[0];
Console.WriteLine(firstChar); // Outputs: H
Dim msg As String = "Hello, World!"
Dim span1 As ReadOnlySpan(Of Char) = msg.AsSpan()
' Read-only manipulation
Dim firstChar As Char = span1(0)
Console.WriteLine(firstChar) ' Outputs: H
2. 处理子字符串
在 ReadOnlySpan 上使用 Slice
// Example usage of Slice method on ReadOnlySpan<char>
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ReadOnlySpan<char> substringSpan = spanFromString.Slice(7, 6); // Extracts 'String'
// Example usage of Slice method on ReadOnlySpan<char>
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ReadOnlySpan<char> substringSpan = spanFromString.Slice(7, 6); // Extracts 'String'
' Example usage of Slice method on ReadOnlySpan<char>
Dim spanFromString As ReadOnlySpan(Of Char) = "Sample String".AsSpan()
Dim substringSpan As ReadOnlySpan(Of Char) = spanFromString.Slice(7, 6) ' Extracts 'String'
3. 将子字符串传递给方法
将 ReadOnlySpan
void ProcessSubstringfromReadOnlySpan(ReadOnlySpan<char> substring)
{
// Perform operations on the substring
}
// Usage
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(7, 6));
void ProcessSubstringfromReadOnlySpan(ReadOnlySpan<char> substring)
{
// Perform operations on the substring
}
// Usage
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(7, 6));
Private Sub ProcessSubstringfromReadOnlySpan(ByVal substring As ReadOnlySpan(Of Char))
' Perform operations on the substring
End Sub
' Usage
Private spanFromString As ReadOnlySpan(Of Char) = "Sample String".AsSpan()
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(7, 6))
4. 在字符串中搜索
使用 IndexOf() 在 ReadOnlySpan<char> 中搜索字符串。
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
在与操作字符 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))
ReadOnlySpan
Span 限制
虽然 C# 中的 Span 是一种强大的特性,具备众多优势,但在连续和非连续内存的背景下,的确有其某些限制和考虑。 让我们探讨这些限制:
1. 连续内存缓冲区
1.1 无自动内存管理
Span 不管理其指向的内存。 这意味着如果底层托管内存被释放或超出范围,使用 Span
1.2 无垃圾收集
由于 Span
1.3 边界检查被禁用
Span
1.4 不支持非连续内存
Span
1.5 不是所有操作都支持
虽然 Span
1.6 限制的平台兼容性
虽然 Span
2. 非连续内存缓冲区
2.1 非连续内存支持有限
ReadOnlySpan
2.2 结构限制
某些依赖不连续内存的数据结构或场景可能与 ReadOnlySpan
2.3 复杂的指针操作
在涉及不连续内存的情况下,特别是那些需要复杂指针运算的情况,ReadOnlySpan
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 分配了一块非托管内存,然后使用从非托管内存中获得的指针创建了 Span<byte>。 这允许我们使用熟悉的 Span API 与非托管内存交互。 需要注意的是,在处理非托管内存时,您有责任管理内存的分配和释放。
复制数据到及从非托管内存
Span 提供了诸如 Slice、CopyTo 和 ToArray 之类的方法,可用于在托管内存和非托管内存之间高效地复制数据。
using System;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
// Managed array to copy data from
int[] sourceArray = { 1, 2, 3, 4, 5 };
// Allocate unmanaged memory for the destination data
IntPtr destinationPointer = Marshal.AllocHGlobal(sourceArray.Length * sizeof(int));
try
{
// Create a Span<int> from the source array
Span<int> sourceSpan = sourceArray;
// Create a Span<int> from the allocated unmanaged memory
Span<int> destinationSpan = new Span<int>(destinationPointer.ToPointer(), sourceArray.Length);
// Copy data from the source Span<int> to the destination Span<int>
sourceSpan.CopyTo(destinationSpan);
// Print the values in the destination memory
Console.WriteLine("Values in the destination memory:");
foreach (var value in destinationSpan)
{
Console.Write($"{value} ");
}
}
finally
{
// Deallocate the unmanaged memory when done
Marshal.FreeHGlobal(destinationPointer);
}
}
}
using System;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
// Managed array to copy data from
int[] sourceArray = { 1, 2, 3, 4, 5 };
// Allocate unmanaged memory for the destination data
IntPtr destinationPointer = Marshal.AllocHGlobal(sourceArray.Length * sizeof(int));
try
{
// Create a Span<int> from the source array
Span<int> sourceSpan = sourceArray;
// Create a Span<int> from the allocated unmanaged memory
Span<int> destinationSpan = new Span<int>(destinationPointer.ToPointer(), sourceArray.Length);
// Copy data from the source Span<int> to the destination Span<int>
sourceSpan.CopyTo(destinationSpan);
// Print the values in the destination memory
Console.WriteLine("Values in the destination memory:");
foreach (var value in destinationSpan)
{
Console.Write($"{value} ");
}
}
finally
{
// Deallocate the unmanaged memory when done
Marshal.FreeHGlobal(destinationPointer);
}
}
}
Imports System
Imports System.Runtime.InteropServices
Friend Class Program
Shared Sub Main()
' Managed array to copy data from
Dim sourceArray() As Integer = { 1, 2, 3, 4, 5 }
' Allocate unmanaged memory for the destination data
Dim destinationPointer As IntPtr = Marshal.AllocHGlobal(sourceArray.Length * Len(New Integer()))
Try
' Create a Span<int> from the source array
Dim sourceSpan As Span(Of Integer) = sourceArray
' Create a Span<int> from the allocated unmanaged memory
Dim destinationSpan As New Span(Of Integer)(destinationPointer.ToPointer(), sourceArray.Length)
' Copy data from the source Span<int> to the destination Span<int>
sourceSpan.CopyTo(destinationSpan)
' Print the values in the destination memory
Console.WriteLine("Values in the destination memory:")
For Each value In destinationSpan
Console.Write($"{value} ")
Next value
Finally
' Deallocate the unmanaged memory when done
Marshal.FreeHGlobal(destinationPointer)
End Try
End Sub
End Class
在此示例中:
Marshal.AllocHGlobal为目标数据分配非托管内存。new Span<int>(destinationPointer.ToPointer(), sourceArray.Length)从已分配的非托管内存创建Span<int>。sourceSpan.CopyTo(destinationSpan)方法将数据从托管数组复制到非托管内存。- 通过打印目标内存中的值验证复制操作。
Marshal.FreeHGlobal(destinationPointer)方法用于在完成后释放非托管内存。
使用不安全代码
在处理非托管内存时,您也可以使用带有指针的不安全代码。 在这种情况下,可以使用 Unsafe.AsPointer() 方法从 Span 获取指针。
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
class Program
{
static void Main()
{
const int bufferSize = 100;
IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
// Create a Span from the unmanaged memory
Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
// Use unsafe code to work with pointers
unsafe
{
byte* pointer = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
// Use the pointer as needed...
}
// Don't forget to free the unmanaged memory when done
Marshal.FreeHGlobal(unmanagedMemory);
}
}
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
class Program
{
static void Main()
{
const int bufferSize = 100;
IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
// Create a Span from the unmanaged memory
Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
// Use unsafe code to work with pointers
unsafe
{
byte* pointer = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
// Use the pointer as needed...
}
// Don't forget to free the unmanaged memory when done
Marshal.FreeHGlobal(unmanagedMemory);
}
}
Imports System
Imports System.Runtime.InteropServices
Imports System.Runtime.CompilerServices
Friend Class Program
Shared Sub Main()
Const bufferSize As Integer = 100
Dim unmanagedMemory As IntPtr = Marshal.AllocHGlobal(bufferSize)
' Create a Span from the unmanaged memory
Dim span As New Span(Of Byte)(unmanagedMemory.ToPointer(), bufferSize)
' Use unsafe code to work with pointers
'INSTANT VB TODO TASK: C# 'unsafe' code is not converted by Instant VB:
' unsafe
' {
' byte* pointer = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
' ' Use the pointer as needed...
' }
' Don't forget to free the unmanaged memory when done
Marshal.FreeHGlobal(unmanagedMemory)
End Sub
End Class
在此示例中,我们使用 Unsafe.AsPointer 方法从 Span 获取指针。 这允许我们在直接处理指针时使用不安全代码。
请记住,在处理非托管内存时,正确管理内存的分配和释放以避免内存泄漏至关重要。 始终使用适当的方法释放非托管内存,例如 Marshal.FreeHGlobal()。 此外,使用不安全代码时务必小心,因为如果处理不当可能会引入潜在的安全风险。
Span 和异步方法调用
在 C# 中将 Span 与异步方法结合使用,适用于处理大量数据或 I/O 操作。 目标是不进行不必要的数据拷贝高效处理异步操作。 让我们探讨如何在异步场景中利用 Span:
1. 异步 I/O 操作:
在处理异步 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 库,可使用 C# 代码动态生成精美的 PDF 文档。 IronPDF 提供多种功能,包括从 HTML 生成 PDF、HTML 内容转 PDF、合并或拆分 PDF 文件等。
IronPDF 的主要功能是其 HTML 到 PDF 功能,可以保留布局和样式。 它可以从网页内容生成 PDF,让它非常适合报告、发票和文档。 此工具支持将 HTML 文件、URL 和 HTML 字符串转换为 PDF 文件。
using IronPdf;
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
// 1. Convert HTML String to PDF
var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");
// 2. Convert HTML File to PDF
var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");
// 3. Convert URL to PDF
var url = "http://ironpdf.com"; // Specify the URL
var pdfFromUrl = renderer.RenderUrlAsPdf(url);
pdfFromUrl.SaveAs("URLToPDF.pdf");
}
}
using IronPdf;
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
// 1. Convert HTML String to PDF
var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");
// 2. Convert HTML File to PDF
var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");
// 3. Convert URL to PDF
var url = "http://ironpdf.com"; // Specify the URL
var pdfFromUrl = renderer.RenderUrlAsPdf(url);
pdfFromUrl.SaveAs("URLToPDF.pdf");
}
}
Imports IronPdf
Friend Class Program
Shared Sub Main(ByVal args() As String)
Dim renderer = New ChromePdfRenderer()
' 1. Convert HTML String to PDF
Dim htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>"
Dim pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent)
pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf")
' 2. Convert HTML File to PDF
Dim htmlFilePath = "path_to_your_html_file.html" ' Specify the path to your HTML file
Dim pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath)
pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf")
' 3. Convert URL to PDF
Dim url = "http://ironpdf.com" ' Specify the URL
Dim pdfFromUrl = renderer.RenderUrlAsPdf(url)
pdfFromUrl.SaveAs("URLToPDF.pdf")
End Sub
End Class
安装
IronPDF 可以通过 NuGet 包管理器用于 IronPDF 控制台或使用 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,可在不受异步边界约束的情况下生成优质 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 的限制包括对连续内存的要求,缺乏自动内存管理和垃圾收集,以及不支持不连续内存。开发人员必须手动确保内存的有效性和边界。
如何安装 IronPDF 以便在 C# 中进行 PDF 操作?
可以通过 NuGet 包管理器在 C# 项目中安装 IronPDF。使用诸如 dotnet add package IronPDF 或 Install-Package IronPDF 的命令将其添加到您的项目中。
使用 Span 进行字符串操作的好处是什么?
Span 通过减少内存分配和复制来实现高效的字符串操作。当处理大量字符串数据的性能关键代码时,这尤为有利。
IronPDF 是否提供试用版本?
是的,IronPDF 提供试用许可证,可以通过提供您的电子邮件获取。试用许可证密钥应放置在 appsettings.json 文件中以使用该库。
Span 可以用于异步方法调用吗?
是的,Span 可以用于异步方法调用,以便无不必要复制地高效处理数据。这在 I/O 操作和文件处理中特别有用,可利用 Memory 或 Span。




