푸터 콘텐츠로 바로가기
.NET 도움말

C# Span (How It Works For Developers)

Span is a type introduced in C# 7.2 as part of the Span struct in the System namespace. It is designed to represent a contiguous region of arbitrary memory. Unlike arrays or collections such as managed heap, Span does not own the stack memory or region of memory it points to; instead, it provides a lightweight view over existing memory blocks. This characteristic makes Span particularly powerful for scenarios where you need to work with memory buffers efficiently without incurring additional overhead and unsafe code scenarios. Later in this article, we shall also see the introduction to the IronPDF library from Iron Software.

Key Characteristics of Span

1. Memory Management

Span in C# allows developers to work with memory directly without resorting to traditional heap allocations. It offers a way to create slices of memory from existing arrays or other memory sources, eliminating the need for additional memory copies.

2. Zero-Copy Abstractions

One of the standout features of C# Span is its zero-copy abstractions. Instead of duplicating data, Span provides a way to reference existing memory efficiently. This is particularly beneficial for scenarios where copying large amounts of data would be impractical or too costly.

3. Pointer-Like Operations

While C# has traditionally been a high-level, safe language, Span introduces a degree of low-level memory manipulation akin to working with pointers in languages like C or C++. Developers can perform pointer-like operations without sacrificing the safety and managed nature of C#.

4. Immutable Nature

Despite its capabilities for low-level memory access, C# Span remains immutable. This means that, while it allows manipulation of memory, it enforces safety by preventing unintended modifications.

Example

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

While Span is mutable and allows for modifications to the underlying data, ReadOnlySpan is an immutable view of memory. It provides a read-only interface to a contiguous region of memory, making it suitable for scenarios where you only need to read the data without modifying it.

Here are some key points.

1. Read-Only View

As the name suggests, ReadOnlySpan allows you to create a read-only view of a block of memory. This means you cannot modify the elements through a ReadOnlySpan.

2. Memory Representation

Like Span, ReadOnlySpan does not own the memory it points to. It refers to existing memory and can point to arrays, stack-allocated memory, or native memory.

3. Performance Benefits

Like Span, ReadOnlySpan can offer better performance compared to traditional collection types, especially when dealing with large amounts of data, as it reduces the need for copying.

4. No Bounds Checking

As with Span, ReadOnlySpan does not perform bounds checking. It is the developer's responsibility to ensure that operations stay within the bounds of the underlying memory.

5. Usage with Array Slicing

ReadOnlySpan supports slicing, allowing you to create sub-spans that reference a portion of the original memory.

Example

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

There are many different ways to create ReadOnlySpan and work with it. Below are some examples.

1. Creating ReadOnlySpan from String

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. Working with Substrings

Use Slice on the ReadOnlySpan

// 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. Passing Substring to a Method

Pass ReadOnlySpan as a parameter to the method.

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. Searching within a String

ReadOnlySpan for searching within a string with IndexOf().

ReadOnlySpan<char> stringSpan = "Hello, World!".AsSpan();
int index = stringSpan.IndexOf('W');
Console.WriteLine(index); // Outputs: 7
ReadOnlySpan<char> stringSpan = "Hello, World!".AsSpan();
int index = stringSpan.IndexOf('W');
Console.WriteLine(index); // Outputs: 7
$vbLabelText   $csharpLabel

5. Using Memory-Mapped Files

ReadOnlySpan can be more efficient with memory-mapped files.

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. Efficient String Manipulation

ReadOnlySpan can be used for efficient string manipulation.

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. Passing Substring to APIs

When working with external libraries or APIs that operate on character spans.

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 provides a way to work with strings more efficiently, especially in scenarios where memory allocations and copying should be minimized. It's a powerful tool for optimizing performance-critical code and can be particularly beneficial when dealing with large amounts of string data.

Span Limitations

While Span in C# is a powerful feature with numerous advantages, it does come with certain limitations and considerations, particularly in the context of contiguous and non-contiguous memory. Let's explore these limitations:

1. Contiguous Memory Buffers

1.1 No Automatic Memory Management

Span does not manage the memory it points to. This means that if the underlying managed memory is released or goes out of scope, using the Span will result in undefined behavior or potential crashes. Developers need to ensure that the underlying memory is still valid when using a Span.

1.2 No Garbage Collection

Since Span does not own memory, it is not subject to garbage collection. Therefore, you need to be careful when working with stack-allocated memory or memory that has a shorter lifetime than the Span itself.

1.3 Bounds Checking is Disabled

Span and ReadOnlySpan do not perform bounds checking by default. This can lead to accessing invalid memory locations if not used carefully. Developers need to manually ensure that the operations on a Span stay within the bounds of the underlying memory.

1.4 No Support for Non-Contiguous Memory

Span is designed to work with contiguous memory. If you have non-contiguous memory or need to represent more complex data structures, Span may not be the most appropriate choice.

1.5 Not All Operations are Supported

While Span supports many common operations like slicing, indexing, and iterating, not all operations are supported. For example, you cannot resize a Span, and certain operations that involve changing the length of the underlying memory are not allowed.

1.6 Limited Platform Compatibility

While Span is part of the .NET Standard and .NET Core, it may not be available on all platforms or environments. It is crucial to ensure that your target platform supports Span if you plan to use it in your code.

2. Non-Contiguous Memory Buffers

2.1 Limited Support for Non-Contiguous Memory

ReadOnlySpan is primarily designed to work seamlessly with contiguous memory blocks or buffers. It may not be the most suitable choice for scenarios where non-contiguous memory buffers or structures with gaps in memory are involved.

2.2 Structural Limitations

Certain data structures or scenarios that rely on non-contiguous memory may not align well with ReadOnlySpan. For instance, data structures like linked lists or graph structures might not be well-suited due to the contiguous memory requirement of ReadOnlySpan.

2.3 Complex Pointer Operations

In situations involving non-contiguous memory, particularly those requiring intricate pointer arithmetic, ReadOnlySpan might not offer the same low-level control and flexibility as raw pointers in languages like C++. In such cases, utilizing unsafe code with pointers could be more appropriate.

2.4 Lack of Direct Support in Some APIs

Similar to contiguous memory, it's important to note that not all APIs or libraries may directly support non-contiguous memory represented by ReadOnlySpan. Adapting to such scenarios might necessitate additional intermediate steps or conversions to ensure compatibility.

Span and Unmanaged Memory

In C#, Span can be effectively used with unmanaged memory to perform memory-related operations in a controlled and efficient manner. Unmanaged memory refers to memory that is not managed by the .NET runtime's garbage collector, and it often involves using native memory allocations and deallocations. Here's how Span can be utilized with unmanaged memory in C#.

Allocating Unmanaged Memory

To allocate unmanaged memory, you can use the System.Runtime.InteropServices.MemoryMarshal class. The Marshal.AllocHGlobal method allocates memory and returns a pointer to the allocated block. The memory allocated or memory address is held in an unmanagedMemory pointer and will have read-write access. The contiguous regions of memory can be easily accessed.

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

In the above source code, we allocate a block of unmanaged memory using Marshal.AllocHGlobal and then create a Span<byte> using the pointer obtained from the unmanaged memory. This allows us to work with unmanaged memory using the familiar Span API. It's important to note that when working with unmanaged memory, you are responsible for managing the allocation and deallocation of the memory.

Copying Data to and from Unmanaged Memory

Span provides methods like Slice, CopyTo, and ToArray that can be used for copying data between managed and unmanaged memory efficiently.

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

In this example:

  • Marshal.AllocHGlobal allocates unmanaged memory for the destination data.
  • new Span<int>(destinationPointer.ToPointer(), sourceArray.Length) creates a Span<int> from the allocated unmanaged memory.
  • The sourceSpan.CopyTo(destinationSpan) method copies the data from the managed array to the unmanaged memory.
  • The values in the destination memory are printed to verify the copy operation.
  • The Marshal.FreeHGlobal(destinationPointer) method is used to deallocate the unmanaged memory when done.

Using Unsafe Code

When dealing with unmanaged memory, you may also use unsafe code with pointers. In such cases, you can obtain a pointer from the Span using the Unsafe.AsPointer() method.

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

In this example, we use the Unsafe.AsPointer method to obtain a pointer from the Span. This allows us to use unsafe code when working with pointers directly.

Remember, when working with unmanaged memory, it's crucial to manage the allocation and deallocation properly to avoid memory leaks. Always free unmanaged memory using appropriate methods, such as Marshal.FreeHGlobal(). Additionally, exercise caution when using unsafe code, as it can introduce potential security risks if not handled properly.

Span and Asynchronous Method Calls

Using Span in conjunction with asynchronous method calls in C# is a powerful combination, especially when dealing with large amounts of data or I/O operations. The goal is to handle asynchronous operations without unnecessary copying of data efficiently. Let's explore how you can leverage Span in asynchronous scenarios:

1. Asynchronous I/O Operations:

When dealing with asynchronous I/O operations, such as reading or writing data to a stream, you can use Memory or Span to efficiently work with the data without creating additional buffers.

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

In this example, the ReadAsync method asynchronously reads data from a stream into the buffer. The ProcessData method then processes the data directly from the Span without copying it to another buffer.

2. Asynchronous File Operations:

Similar to I/O operations, when dealing with asynchronous file operations, you can use Span to efficiently process data without additional copying.

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

Here, the ReadAsync method reads data from a file stream into the buffer, and the ProcessData method processes the data directly from the Span.

3. Asynchronous Task Processing:

When working with asynchronous tasks that produce or consume data, you can use Memory or Span to avoid unnecessary copying.

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

In this example, the ProcessDataAsync method processes the data asynchronously and returns the length of the processed data without requiring additional copies.

Introducing IronPDF

IronPDF library overview is the latest C# PDF library from Iron Software which can be used to generate beautiful PDF documents on the fly dynamically using C# code. IronPDF provides a variety of features such as PDF generation from HTML, converting HTML content to PDF, merging or splitting PDF files, etc.

IronPDF’s main feature is its HTML to PDF functionality, which preserves layouts and styles. It can generate PDFs from web content, making it great for reports, invoices, and documentation. This tool supports converting HTML files, URLs, and HTML strings to PDF files.

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

Installation

IronPDF can be installed using the NuGet package manager for IronPDF console or using the Visual Studio package manager.

dotnet add package IronPdf
// Or
Install-Package IronPdf

C# Span (How It Works For Developers): Figure 1 - Install IronPDF using NuGet Package Manager by searching ironpdf in the search bar of NuGet Package Manager

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

In this example, we are using Span along with IronPDF to generate a PDF document.

Output:

C# Span (How It Works For Developers): Figure 2 - Console Output

Generated PDF:

C# Span (How It Works For Developers): Figure 3 - PDF Output

Licensing (Free Trial Available)

IronPDF license information. This key needs to be placed in appsettings.json.

"IronPdf.LicenseKey": "your license key"

Provide your email to get a trial license.

Conclusion

Span in C# provides a powerful and efficient way to work with memory, offering benefits in terms of performance and flexibility. Its non-owning, contiguous nature makes it particularly suitable for scenarios where minimizing memory allocations and copying is crucial. By leveraging Span, developers can achieve better performance in a variety of applications, ranging from string manipulation to high-performance numeric processing. By understanding its features and considering its limitations, developers can leverage Span for various memory manipulation tasks safely and efficiently. Along with IronPDF library overview, it can be used to generate awesome PDF documents without await and yield boundaries.

Please visit IronPDF's Quick-Start Documentation Page page.

자주 묻는 질문

C#에서 Span란 무엇이며 왜 중요한가요?

Span는 C# 7.2에 도입된 유형으로, 연속된 메모리 영역을 나타냅니다. 개발자가 힙 할당의 오버헤드 없이 저수준 메모리 연산을 효율적으로 수행하면서 C#의 안전성과 성능을 유지할 수 있기 때문에 중요합니다.

Span는 C#에서 메모리 조작을 어떻게 최적화하나요?

Span는 메모리에 대한 제로 카피 추상화를 제공하여 개발자가 데이터 복제 없이 기존 메모리 블록을 참조할 수 있도록 함으로써 메모리 조작을 최적화합니다. 이는 특히 대량의 데이터를 처리하는 애플리케이션에서 성능 향상으로 이어집니다.

Span와 ReadOnlySpan의 차이점은 무엇인가요?

Span는 메모리에 대한 변경 가능한 보기로 수정이 가능한 반면, ReadOnlySpan는 읽기 전용 보기를 제공합니다. 데이터를 수정해서는 안 되는 경우, 데이터 무결성을 보장하면서 비슷한 성능 이점을 제공하는 ReadOnlySpan를 사용합니다.

C#에서 관리되지 않는 메모리와 함께 Span를 사용할 수 있나요?

예, 포인터에서 관리되지 않는 메모리에 대한 스팬을 생성하여 스팬를 관리되지 않는 메모리와 함께 사용할 수 있습니다. 이를 통해 메모리를 직접 조작하는 동시에 Marshal.AllocHGlobalMarshal.FreeHGlobal과 같은 메서드를 사용하여 적절하게 할당 및 할당 해제되도록 할 수 있습니다.

IronPDF는 PDF 생성을 위해 Span와 어떻게 통합되나요?

IronPDF는 Span와 함께 작동하여 메모리를 효율적으로 관리하고 불필요한 할당을 방지함으로써 PDF를 동적으로 생성할 수 있습니다. 이러한 통합을 통해 개발자는 향상된 성능으로 웹 콘텐츠에서 PDF 문서를 생성할 수 있습니다.

메모리 관리에 Span를 사용할 때의 한계는 무엇인가요?

Span 사용의 한계로는 연속 메모리가 필요하고, 자동 메모리 관리 및 가비지 수집 기능이 없으며, 비연속 메모리를 지원하지 않는다는 점 등이 있습니다. 개발자는 메모리 유효성과 범위를 수동으로 확인해야 합니다.

C#에서 PDF 조작을 위해 IronPDF를 설치하려면 어떻게 해야 하나요?

IronPDF는 NuGet 패키지 관리자를 사용하여 C# 프로젝트에 설치할 수 있습니다. dotnet add package IronPdf 또는 Install-Package IronPdf와 같은 명령을 사용하여 프로젝트에 추가하세요.

문자열 조작에 Span를 사용하면 어떤 이점이 있나요?

Span는 메모리 할당과 복사를 최소화하여 효율적인 문자열 조작을 가능하게 합니다. 이는 대량의 문자열 데이터를 처리하는 성능에 중요한 코드에서 특히 유용합니다.

IronPDF에 대한 평가판이 있나요?

예, IronPDF는 이메일을 제공하여 받을 수 있는 평가판 라이선스를 제공합니다. 평가판 라이선스 키는 라이브러리를 사용하려면 appsettings.json 파일에 배치해야 합니다.

비동기 메서드 호출에 Span를 사용할 수 있나요?

예, 비동기 메서드 호출에서 Span를 사용하여 불필요한 복사 없이 데이터를 효율적으로 처리할 수 있습니다. 이는 특히 I/O 작업과 파일 처리에 유용하며, Memory 또는 Span를 활용할 때 유용합니다.

커티스 차우
기술 문서 작성자

커티스 차우는 칼턴 대학교에서 컴퓨터 과학 학사 학위를 취득했으며, Node.js, TypeScript, JavaScript, React를 전문으로 하는 프론트엔드 개발자입니다. 직관적이고 미적으로 뛰어난 사용자 인터페이스를 만드는 데 열정을 가진 그는 최신 프레임워크를 활용하고, 잘 구성되고 시각적으로 매력적인 매뉴얼을 제작하는 것을 즐깁니다.

커티스는 개발 분야 외에도 사물 인터넷(IoT)에 깊은 관심을 가지고 있으며, 하드웨어와 소프트웨어를 통합하는 혁신적인 방법을 연구합니다. 여가 시간에는 게임을 즐기거나 디스코드 봇을 만들면서 기술에 대한 애정과 창의성을 결합합니다.