跳至頁尾內容
.NET 幫助

C# Span(開發者如何理解其工作原理)

Span 是 C# 7.2 中引入的一種類型,是 System 命名空間中 Span結構的一部分。 其設計目的在於表示任意記憶體的連續區域。 與管理堆等陣列或集合不同,Span 並不擁有堆疊記憶體或其指向的記憶體區域; 相反地,它提供了一個輕量級的檢視,超過現有的記憶區塊。 此特性使得 Span 在需要有效率地使用記憶體緩衝區,而不會產生額外開銷和不安全程式碼的情況下,功能特別強大。 在本文後面,我們還將看到 Iron SoftwareIronPDF 函式庫的介紹。

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);
        }
    }
}
$vbLabelText   $csharpLabel

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;
    }
}
$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
$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'
$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));
$vbLabelText   $csharpLabel

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
$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);
            }
        }
    }
}
$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
$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));
$vbLabelText   $csharpLabel

ReadOnlySpan提供了一種更有效率地處理字串的方式,尤其是在應該盡量減少記憶體分配和複製的情境中。 它是優化效能關鍵程式碼的強大工具,在處理大量字串資料時尤其有利。

跨度限制

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,確保您的目標平台支援 Span是至關重要的。

2.非連續記憶體緩衝區

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

ReadOnlySpan主要設計用來與連續的記憶體區塊或緩衝區無縫運作。 對於涉及非連續記憶體緩衝區或記憶體有空隙的結構的場景,可能不是最適合的選擇。

2.2結構限制

某些依賴於非連續記憶體的資料結構或情境可能與 ReadOnlySpan不太一致。 例如,由於 ReadOnlySpan的連續記憶體需求,像連結清單或圖形結構這樣的資料結構可能不太適合。

2.3複雜指標操作

在涉及非連續記憶體的情況下,特別是那些需要複雜的指標運算的情況下,ReadOnlySpan可能無法提供與 C++ 等語言中原始指標相同的低階控制和彈性。 在這種情況下,利用不安全代碼與指針可能更為合適。

2.4 某些 API 缺乏直接支援。

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

跨和非管理記憶體

在 C# 中,Span 可以有效地與非管理記憶體搭配使用,以受控且有效率的方式執行與記憶體相關的作業。 非管理記憶體指的是未被 .NET runtime 的垃圾回收器管理的記憶體,它通常涉及使用本機記憶體分配和取消分配。 以下是 Span 如何利用 C# 中的非管理記憶體。

分配非管理記憶體

若要分配非管理記憶體,您可以使用 System.Runtime.InteropServices.MemoryMarshal 類別。 Marshal.AllocHGlobal 方法會分配記憶體,並傳回已分配區塊的指標。 分配的記憶體或記憶體位址會以非管理記憶體指針 (unmanagedMemory pointer) 的方式持有,並會具有讀寫存取權。 可輕鬆存取記憶體的連續區域。

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);
    }
}
$vbLabelText   $csharpLabel

在上述原始碼中,我們使用 Marshal.AllocHGlobal 分配一個非管理記憶體區塊,然後再使用從非管理記憶體取得的指標建立一個 Span<byte> 。 這可讓我們使用熟悉的 Span API 來處理非管理記憶體。 值得注意的是,在使用非管理記憶體時,您必須負責管理記憶體的分配和取消分配。

將資料複製到非管理記憶體或從非管理記憶體複製資料。

Span 提供了像 Slice, CopyToToArray 之類的方法,可用於在受管理記憶體和非受管理記憶體之間有效地複製資料。

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);
        }
    }
}
$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);
    }
}
$vbLabelText   $csharpLabel

在這個範例中,我們使用 Unsafe.AsPointer 方法從 Span 取得指標。 這可讓我們在直接處理指標時使用不安全的程式碼。

請記住,在使用非管理記憶體時,妥善管理分配和取消分配以避免記憶體洩漏是非常重要的。 請務必使用適當的方法釋放非管理記憶體,例如 Marshal.FreeHGlobal() 。 此外,使用不安全的程式碼時要小心,因為如果處理不當,可能會帶來潛在的安全風險。

Span 和異步方法呼叫。

在 C# 中將 Span 與異步方法呼叫結合使用是一個強大的組合,尤其是在處理大量資料或 I/O 作業時。 目標是在沒有不必要複製資料的情況下,有效率地處理非同步作業。 讓我們來探討如何在異步情境中利用 Span:

1.異步 I/O 操作: 2.

在處理異步 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
    }
}
$vbLabelText   $csharpLabel

在這個範例中,ReadAsync 方法以異步方式將資料從串流讀取到緩衝區中。 之後,ProcessData 方法會直接處理 Span中的資料,而不會複製到另一個緩衝區。

2. 異步檔案作業: 3.

與 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
    }
}
$vbLabelText   $csharpLabel

在這裡,ReadAsync 方法將資料從檔案串流讀入緩衝區,而 ProcessData 方法則直接從 Span處理資料。

3. 異步任務處理: 4.

在處理產生或消耗資料的異步任務時,您可以使用 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}");
    }
}
$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");
    }
}
$vbLabelText   $csharpLabel

安裝

IronPDF 可使用 NuGet package manager for IronPDF 控制台或 Visual Studio package manager 進行安裝。

dotnet add package IronPdf
// Or
Install-Package IronPdf

C# Span (How It Works For Developers):圖 1 - 使用 NuGet Package Manager 安裝 IronPDF,方法是在 NuGet Package Manager 的搜尋列中搜尋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");
    }
}
$vbLabelText   $csharpLabel

在這個範例中,我們使用 SpanIronPDF 來產生 PDF 文件。

Output:

!a href="/static-assets/pdf/blog/csharp-span/csharp-span-2.webp">C# Span (How It Works For Developers):圖 2 - 控制台輸出

生成的 PDF:

C# Span (How It Works For Developers):圖 3 - PDF 輸出

授權(可免費試用)

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

"IronPdf.LicenseKey": "your license key"

提供您的電子郵件以取得試用授權。

結論

C# 中的 Span提供了一種強大且有效率的方式來處理記憶體,並在效能與彈性方面提供優勢。 它的非擁有性、連續性使其特別適合那些對盡量減少記憶體分配和複製至關重要的場景。 透過利用 Span,開發人員可以在從字串操作到高效能數值處理等各種應用程式中取得更好的效能。 透過瞭解其功能並考慮其限制,開發人員可以利用 Span安全且有效率地執行各種記憶體操作任務。 配合IronPDF函式庫概述,它可以用來產生超棒的 PDF 文件,沒有等待和產量的界限。

請造訪 IronPDF"快速入門文件"頁面

常見問題解答

Span是什麼在 C# 中使用它,以及為什麼它重要?

跨距是 C# 7.2 中引入的一種類型,它表示記憶體中的連續區域。它非常重要,因為它允許開發人員有效地執行底層記憶體操作,而無需進行堆分配,從而保持 C# 的安全性和效能。

Span是如何運作的如何在 C# 中優化記憶體操作?

跨距它透過提供零拷貝記憶體抽象來優化記憶體操作,使開發人員能夠在不複製資料的情況下引用現有記憶體區塊。這顯著提升了效能,尤其是在處理大量資料的應用程式中。

Span 和 Span 有什麼不同?和唯讀跨度?

跨距是記憶體的可變視圖,允許進行修改,而 ReadOnlySpan 則不然。提供唯讀視圖。 ReadOnlySpan當資料不應被修改時,可以使用此方法,在提供類似效能優勢的同時,確保資料完整性。

罐跨距可以在 C# 中使用非託管記憶體嗎?

是的,Span可以透過從指向非託管記憶體的指標建立跨度來使用非託管記憶體。這允許直接操作內存,同時確保使用諸如Marshal.AllocHGlobalMarshal.FreeHGlobal之類的方法正確地分配和釋放內存。

IronPDF 如何與 Span 集成用於生成PDF文件?

IronPDF 可以與 Span 協同工作。透過有效率地管理記憶體並避免不必要的記憶體分配,實現動態產生 PDF 檔案。此整合使開發人員能夠以更高的效能從 Web 內容建立 PDF 文件。

使用 Span 有哪些局限性用於記憶體管理?

使用 Span 的局限性其中包括對連續記憶體的要求、缺乏自動記憶體管理和垃圾回收機制,以及不支援非連續記憶體。開發人員必須手動確保記憶體的有效性和邊界。

如何在C#中安裝IronPDF以進行PDF處理?

可以使用 NuGet 套件管理器將 IronPDF 安裝到 C# 專案中。使用諸如dotnet add package IronPdfInstall-Package IronPdf之類的命令將其新增至您的專案。

使用 Span 有什麼好處?用於字串操作?

跨距它透過最大限度地減少記憶體分配和複製操作,實現了高效的字串操作。這對於需要處理大量字串資料的效能關鍵型程式碼尤其有利。

IronPDF有試用版嗎?

是的,IronPDF 提供試用許可證,您可以透過提供電子郵件地址來取得。試用許可證金鑰需要加入到appsettings.json檔案中才能使用該庫。

罐跨距能否用於異步方法呼叫?

是的,Span可用於非同步方法調用,以有效率地處理數據,避免不必要的複製。這在 I/O 操作和檔案處理中尤其有用,可以充分利用MemorySpan

Jacob Mellor,Team Iron 首席技術官
首席技術長

Jacob Mellor 是 Iron Software 的首席技術官,也是一位富有遠見的工程師,率先開發了 C# PDF 技術。作為 Iron Software 核心程式碼庫的最初開發者,他自公司成立之初便參與塑造了其產品架構,並與執行長 Cameron Rimington 一起將其發展成為一家擁有 50 多名員工、服務於 NASA、特斯拉和全球政府機構的公司。

Jacob 於 1998 年至 2001 年在曼徹斯特大學獲得土木工程一級榮譽學士學位。 1999 年,他在倫敦創辦了自己的第一家軟體公司;2005 年,他創建了自己的第一個 .NET 元件。此後,他專注於解決微軟生態系統中的複雜問題。

他的旗艦產品 IronPDF 和 IronSuite .NET 庫在全球 NuGet 上的安裝量已超過 3000 萬次,其基礎程式碼持續為全球開發者工具提供支援。憑藉 25 年的商業經驗和 41 年的程式設計專長,Jacob 始終致力於推動企業級 C#、Java 和 Python PDF 技術的創新,同時指導下一代技術領導者。