Span C# (Cómo Funciona para Desarrolladores)
Span es un tipo introducido en C# 7.2 como parte de la estructura Span en el espacio de nombres System. Está diseñado para representar una región contigua de memoria arbitraria. A diferencia de los arreglos o colecciones como el montón manejado, Span no posee la memoria de la pila o la región de memoria a la que apunta; en su lugar, proporciona una vista ligera sobre bloques de memoria existentes. Esta característica hace que Span sea particularmente potente en escenarios donde necesitas trabajar con búferes de memoria de manera eficiente sin incurrir en costos adicionales y en situaciones de código inseguro. Más adelante en este artículo, también veremos la introducción a la biblioteca IronPDF de Iron Software.
Características principales de Span
1. Gestión de memoria
Span en C# permite a los desarrolladores trabajar directamente con la memoria sin recurrir a asignaciones tradicionales de montón. Ofrece una forma de crear trozos de memoria a partir de arreglos existentes u otras fuentes de memoria, eliminando la necesidad de copias de memoria adicionales.
2. Abstracciones de copia cero
Una de las características destacadas del Span de C# son sus abstracciones de cero copias. En lugar de duplicar datos, Span proporciona una forma de referenciar la memoria existente de manera eficiente. Esto es particularmente beneficioso en escenarios donde copiar grandes cantidades de datos sería poco práctico o demasiado costoso.
3. Operaciones con punteros
Aunque C# ha sido tradicionalmente un lenguaje de alto nivel y seguro, Span introduce un grado de manipulación de memoria de bajo nivel similar a trabajar con punteros en lenguajes como C o C++. Los desarrolladores pueden realizar operaciones similares a punteros sin sacrificar la seguridad y la naturaleza gestionada de C#.
4. Naturaleza inmutable
A pesar de sus capacidades para el acceso a memoria de bajo nivel, Span de C# sigue siendo inmutable. Esto significa que, aunque permite manipular memoria, garantiza la seguridad al prevenir modificaciones no intencionadas.
Ejemplo
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);
}
}
}SóloLectura
Mientras que Span es mutable y permite modificaciones en los datos subyacentes, ReadOnlySpan es una vista inmutable de la memoria. Proporciona una interfaz de solo lectura a una región contigua de memoria, lo que la hace adecuada para escenarios en los que solo necesitas leer los datos sin modificarlos.
Aquí hay algunos puntos clave.
1. Vista de sólo lectura
Como su nombre indica, ReadOnlySpan te permite crear una vista de solo lectura de un bloque de memoria. Esto significa que no puedes modificar los elementos a través de un ReadOnlySpan.
2. Representación de la memoria
Al igual que Span, ReadOnlySpan no posee la memoria a la que apunta. Se refiere a la memoria existente y puede apuntar a arreglos, memoria asignada en la pila o memoria nativa.
3. Ventajas de rendimiento
Como Span, ReadOnlySpan puede ofrecer un mejor rendimiento en comparación con los tipos de colecciones tradicionales, especialmente al tratar con grandes cantidades de datos, ya que reduce la necesidad de copiar.
4. Sin comprobación de límites
Como con Span, ReadOnlySpan no realiza comprobaciones de límites. Es responsabilidad del desarrollador asegurarse de que las operaciones se mantengan dentro de los límites de la memoria subyacente.
5. Uso con Array Slicing
ReadOnlySpan admite el corte, permitiéndote crear sub-spans que referencian una porción de la memoria original.
Ejemplo
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;
}
}Existen muchas maneras diferentes de crear ReadOnlySpan y trabajar con él. Ejemplos a continuación.
1. Creación de ReadOnlySpan a partir de String
string msg = "Hello, World!";
ReadOnlySpan<char> span1 = msg.AsSpan();
// Read-only manipulation
char firstChar = span1[0];
Console.WriteLine(firstChar); // Outputs: Hstring msg = "Hello, World!";
ReadOnlySpan<char> span1 = msg.AsSpan();
// Read-only manipulation
char firstChar = span1[0];
Console.WriteLine(firstChar); // Outputs: H2. Trabajar con subcadenas
Usar Slice en el 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'3. Pasar una subcadena a un método
Pasar 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));4. Búsqueda dentro de una cadena
ReadOnlySpanIndexOf().
ReadOnlySpan<char> stringSpan = "Hello, World!".AsSpan();
int index = stringSpan.IndexOf('W');
Console.WriteLine(index); // Outputs: 7ReadOnlySpan<char> stringSpan = "Hello, World!".AsSpan();
int index = stringSpan.IndexOf('W');
Console.WriteLine(index); // Outputs: 75. Uso de archivos mapeados en memoria
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);
}
}
}
}6. Manipulación eficiente de cadenas
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: StringSpan<char> newSpan = new char[6];
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan().Slice(7, 6);
spanFromString.CopyTo(newSpan);
Console.WriteLine(new string(newSpan)); // Outputs: String7. Pasar subcadena a API
Cuando se trabaja con bibliotecas o APIs externas que operan con spans de caracteres.
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));ReadOnlySpan
Limitaciones de espacio
Aunque Span en C# es una característica poderosa con numerosas ventajas, también presenta ciertas limitaciones y consideraciones, especialmente en el contexto de la memoria contigua y no contigua. Exploremos estas limitaciones:
1. Búferes de memoria contigua
1.1 Sin gestión automática de memoria
Span no gestiona la memoria a la que apunta. Esto significa que si la memoria administrada subyacente se libera o sale del alcance, usar el Span resultará en un comportamiento indefinido o posibles fallos. Los desarrolladores deben asegurarse de que la memoria subyacente todavía sea válida al usar un Span.
1.2 Sin recolección de elementos no utilizados
Dado que Span no posee memoria, no está sujeta a la recolección de basura. Por lo tanto, debes tener cuidado al trabajar con memoria asignada en la pila o memoria que tiene una vida útil más corta que el Span mismo.
1.3 La comprobación de límites está desactivada
Span y ReadOnlySpan no realizan comprobaciones de límites por defecto. Esto puede llevar a acceder a ubicaciones de memoria no válidas si no se usa con cuidado. Los desarrolladores deben asegurarse manualmente de que las operaciones en un Span se mantengan dentro de los límites de la memoria subyacente.
1.4 Sin soporte para memoria no contigua
Span está diseñado para trabajar con memoria contigua. Si tienes memoria no contigua o necesitas representar estructuras de datos más complejas, Span puede no ser la opción más apropiada.
1.5 No todas las operaciones son compatibles
Mientras que Span admite muchas operaciones comunes como corte, indexación e iteración, no todas las operaciones son compatibles. Por ejemplo, no puedes cambiar el tamaño de un Span, y ciertas operaciones que implican cambiar la longitud de la memoria subyacente no están permitidas.
1.6 Compatibilidad limitada de plataformas
Aunque Span es parte del .NET Standard y .NET Core, puede que no esté disponible en todas las plataformas o entornos. Es crucial asegurarse de que tu plataforma de destino admita Span si planeas usarlo en tu código.
2. Búferes de memoria no contiguos
2.1 Soporte limitado para memoria no contigua
ReadOnlySpan está diseñado principalmente para trabajar de manera fluida con bloques de memoria o búferes contiguos. Puede que no sea la opción más adecuada para escenarios donde están involucrados búferes de memoria no contigua o estructuras con huecos en la memoria.
2.2 Limitaciones estructurales
Ciertas estructuras de datos o escenarios que dependen de memoria no contigua pueden no alinearse bien con ReadOnlySpan. Por ejemplo, estructuras de datos como listas enlazadas o estructuras de gráfico pueden no ser adecuadas debido al requisito de memoria contigua de ReadOnlySpan.
2.3 Operaciones complejas con punteros
En situaciones que involucran memoria no contigua, particularmente aquellas que requieren aritmética de punteros intrincada, ReadOnlySpan podría no ofrecer el mismo control y flexibilidad de bajo nivel que los punteros en lenguajes como C++. En tales casos, podría ser más apropiado utilizar código inseguro con punteros.
2.4 Falta de soporte directo en algunas API
Al igual que la memoria contigua, es importante notar que no todas las API o bibliotecas pueden admitir directamente la memoria no contigua representada por ReadOnlySpan. Adaptarse a tales escenarios podría requerir pasos o conversiones intermedias adicionales para garantizar la compatibilidad.
Span y memoria no gestionada
En C#, Span puede ser utilizado eficazmente con memoria no gestionada para realizar operaciones relacionadas con la memoria de manera controlada y eficiente. Memoria no gestionada se refiere a la memoria que no es gestionada por el recolector de basura del runtime de .NET, y a menudo involucra el uso de asignaciones y desasignaciones de memoria nativa. Aquí se explica cómo puede utilizarse Span con memoria no gestionada en C#.
Asignación de memoria no gestionada
Para asignar memoria no gestionada, puedes usar la clase System.Runtime.InteropServices.MemoryMarshal. El método Marshal.AllocHGlobal asigna memoria y devuelve un puntero al bloque asignado. La memoria asignada o la dirección de memoria se mantiene en un puntero unmanagedMemory y tendrá acceso de lectura-escritura. Las regiones contiguas de memoria pueden ser fácilmente accesadas.
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);
}
}En el código fuente anterior, asignamos un bloque de memoria no gestionada usando Marshal.AllocHGlobal y luego creamos un Span<byte> usando el puntero obtenido de la memoria no gestionada. Esto nos permite trabajar con memoria no gestionada usando la API familiar de Span. Es importante notar que al trabajar con memoria no gestionada, eres responsable de gestionar la asignación y desasignación de la memoria.
Copiar datos desde y hacia memoria no gestionada
Span proporciona métodos como Slice, CopyTo y ToArray que se pueden usar para copiar datos entre memoria gestionada y no gestionada de manera eficiente.
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);
}
}
}En este ejemplo:
Marshal.AllocHGlobalasigna memoria no gestionada para los datos de destino.new Span<int>(destinationPointer.ToPointer(), sourceArray.Length)crea unSpan<int>a partir de la memoria no gestionada asignada.- El método
sourceSpan.CopyTo(destinationSpan)copia los datos desde el arreglo gestionado a la memoria no gestionada. - Los valores en la memoria de destino se imprimen para verificar la operación de copia.
- El método
Marshal.FreeHGlobal(destinationPointer)se usa para desasignar la memoria no gestionada cuando se termina.
Uso de código no seguro
Al tratar con memoria no gestionada, también podrías usar código inseguro con punteros. En tales casos, puedes obtener un puntero del Span usando el método Unsafe.AsPointer().
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);
}
}En este ejemplo, usamos el método Unsafe.AsPointer para obtener un puntero del Span. Esto nos permite usar código inseguro cuando trabajamos directamente con punteros.
Recuerda, al trabajar con memoria no gestionada, es crucial gestionar adecuadamente la asignación y desasignación para evitar fugas de memoria. Libera siempre la memoria no gestionada usando métodos apropiados, como Marshal.FreeHGlobal(). Además, ejerce precaución al usar código inseguro, ya que puede introducir riesgos potenciales de seguridad si no se maneja adecuadamente.
Span y llamadas a métodos asíncronos
Usar Span junto con llamadas asíncronas a métodos en C# es una combinación poderosa, especialmente al tratar con grandes cantidades de datos o operaciones de E/S. El objetivo es manejar operaciones asíncronas sin copias innecesarias de datos de manera eficiente. Exploramos cómo puedes aprovechar Span en escenarios asíncronos:
1. Operaciones de E/S asíncronas:
Al tratar con operaciones de I/O asincrónicas, como leer o escribir datos en un flujo, puedes usar Memory o Span para trabajar con los datos de manera eficiente sin crear buffers adicionales.
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
}
}En este ejemplo, el método ReadAsync lee asíncronamente datos desde un flujo hacia el buffer. El método ProcessData luego procesa los datos directamente desde el Span
2. Operaciones de archivos asíncronas:
Similar a las operaciones de E/S, al tratar con operaciones asíncronas de archivos, puedes usar Span para procesar datos de manera eficiente sin copias adicionales.
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
}
}Aquí, el método ReadAsync lee datos desde un flujo de archivos hacia el buffer, y el método ProcessData procesa los datos directamente desde el Span
3. Procesamiento de tareas asíncronas:
Al trabajar con tareas asincrónicas que producen o consumen datos, puedes usar Memory o Span para evitar copias innecesarias.
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}");
}
}En este ejemplo, el método ProcessDataAsync procesa los datos asíncronamente y devuelve la longitud de los datos procesados sin requerir copias adicionales.
Presentando IronPDF
Resumen de la biblioteca IronPDF es la última biblioteca PDF de C# de Iron Software que puede usarse para generar documentos PDF hermosos sobre la marcha de manera dinámica usando código C#. IronPDF proporciona una variedad de características como generación de PDF desde HTML, convertir contenido HTML a PDF, fusionar o dividir archivos PDF, etc.
La característica principal de IronPDF es su funcionalidad HTML a PDF, que preserva los diseños y estilos. Puede generar PDFs a partir de contenido web, lo que lo hace excelente para informes, facturas y documentación. Esta herramienta es compatible con la conversión de archivos HTML, URLs y cadenas HTML a archivos 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");
}
}Instalación
IronPDF se puede instalar utilizando el administrador de paquetes NuGet para IronPDF en la consola o usando el administrador de paquetes de 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");
}
}En este ejemplo, estamos usando Span junto con IronPDF para generar un documento PDF.
Output:

PDF Generado:

Licencia (Prueba gratuita disponible)
Información de la licencia de IronPDF. Esta clave debe colocarse en appsettings.json.
"IronPdf.LicenseKey": "your license key"Proporcione su correo para una licencia de prueba.
Conclusión
Span en C# proporciona una forma poderosa y eficiente de trabajar con memoria, ofreciendo beneficios en términos de rendimiento y flexibilidad. Su naturaleza no propietaria y contigua lo hace particularmente adecuado para escenarios donde minimizar las asignaciones de memoria y las copias es crucial. Al aprovechar Span, los desarrolladores pueden lograr un mejor rendimiento en una variedad de aplicaciones, que van desde la manipulación de cadenas hasta el procesamiento numérico de alto rendimiento. Al comprender sus características y considerar sus limitaciones, los desarrolladores pueden aprovechar Span para diversas tareas de manipulación de memoria de manera segura y eficiente. Junto con resumen de la biblioteca IronPDF, puede usarse para generar documentos PDF impresionantes sin esperar y rendir límites.
Por favor, visita la página de Documentación de Inicio Rápido de IronPDF.
Preguntas Frecuentes
¿Qué es Span en C# y por qué es importante?
Span es un tipo introducido en C# 7.2 que representa una región contigua de memoria. Es importante porque permite a los desarrolladores realizar operaciones de memoria de bajo nivel de manera eficiente sin la sobrecarga de las asignaciones en el heap, manteniendo la seguridad y el rendimiento de C#.
¿Cómo optimiza Span la manipulación de memoria en C#?
Span optimiza la manipulación de memoria proporcionando una abstracción de cero copias sobre la memoria, permitiendo a los desarrolladores referenciar bloques de memoria existentes sin duplicar datos. Esto lleva a mejoras en el rendimiento, particularmente en aplicaciones que manejan grandes volúmenes de datos.
¿Cuál es la diferencia entre Span y ReadOnlySpan?
Span es una vista mutable de la memoria, permitiendo modificaciones, mientras que ReadOnlySpan proporciona una vista de solo lectura. ReadOnlySpan se utiliza cuando los datos no deben ser modificados, ofreciendo beneficios de rendimiento similares a la vez que asegura la integridad de los datos.
¿Puede Span ser utilizado con memoria no administrada en C#?
Sí, Span puede ser utilizado con memoria no administrada creando un span desde un puntero a memoria no administrada. Esto permite la manipulación directa de la memoria mientras se asegura que está correctamente asignada y liberada usando métodos como Marshal.AllocHGlobal y Marshal.FreeHGlobal.
¿Cómo se integra IronPDF con Span para la generación de PDF?
IronPDF puede trabajar junto a Span para generar PDFs dinámicamente al gestionar memoria de manera eficiente y evitar asignaciones innecesarias. Esta integración permite a los desarrolladores crear documentos PDF a partir de contenido web con un rendimiento mejorado.
¿Cuáles son las limitaciones del uso de Span para la gestión de memoria?
Las limitaciones del uso de Span incluyen el requisito de memoria contigua, la falta de gestión automática de memoria y recolección de basura, y la no compatibilidad con memoria no contigua. Los desarrolladores deben garantizar manualmente la validez y los límites de la memoria.
¿Cómo se puede instalar IronPDF para la manipulación de PDF en C#?
IronPDF se puede instalar en un proyecto C# usando el gestor de paquetes NuGet. Usa comandos como dotnet add package IronPdf o Install-Package IronPdf para añadirlo a tu proyecto.
¿Cuáles son los beneficios de usar Span para la manipulación de cadenas?
Span permite una manipulación eficiente de cadenas al minimizar las asignaciones de memoria y la copia. Esto es particularmente beneficioso en código crítico para el rendimiento donde se procesa una gran cantidad de datos de cadenas.
¿Hay una versión de prueba disponible para IronPDF?
Sí, IronPDF ofrece una licencia de prueba que se puede obtener proporcionando tu correo electrónico. La clave de la licencia de prueba debe colocarse en el archivo appsettings.json para usar la biblioteca.
¿Puede Span ser utilizado en llamadas de método asíncronas?
Sí, Span puede ser utilizado en llamadas de método asíncronas para manejar datos de manera eficiente sin copias innecesarias. Esto es particularmente útil en operaciones de E/S y procesamiento de archivos, aprovechando Memory o Span.








