AYUDA .NET

C# Span (Cómo funciona para desarrolladores)

Span es un tipo introducido en C# 7.2 como parte de la estructura Spanen 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 managed heap, Span no posee la memoria de pila o la región de memoria a la que apunta; en su lugar, proporciona una vista ligera sobre los bloques de memoria existentes. Esta característica hace que Span sea especialmente potente para escenarios en los que se necesita trabajar con búferes de memoria de forma eficiente sin incurrir en sobrecarga adicional y escenarios 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 clave de Span

1. Gestión de la memoria

Span en C# permite a los desarrolladores trabajar directamente con la memoria sin recurrir a las tradicionales asignaciones de heap. Ofrece una forma de crear porciones de memoria a partir de matrices existentes u otras fuentes de memoria, eliminando la necesidad de copias de memoria adicionales.

2. Abstracciones de copia cero

Una de las características más destacadas de C# Span son sus abstracciones de copia cero. En lugar de duplicar datos, Span proporciona una forma de referenciar la memoria existente de forma eficiente. Esto es especialmente beneficioso en situaciones en las que copiar grandes cantidades de datos sería poco práctico o demasiado costoso.

3. Operaciones con puntero

Aunque C# ha sido tradicionalmente un lenguaje de alto nivel y seguro, Span introduce cierto grado de manipulación de memoria de bajo nivel similar al trabajo con punteros en lenguajes como C o C++. Los desarrolladores pueden realizar operaciones de tipo puntero sin sacrificar la seguridad y la naturaleza gestionada de C#.

4. Naturaleza inmutable

A pesar de sus capacidades de acceso a memoria de bajo nivel, C# Span sigue siendo inmutable. Esto significa que, aunque permite la manipulación de la memoria, refuerza la seguridad impidiendo modificaciones no intencionadas.

Ejemplo

using System;
class Program
{
    void Main()
    {
        int[] array = { 1, 2, 3, 4, 5 };
        // Create a span that points to the entire array
        Span<int> span = array;
        // Modify the data using the span
        span[2] = 10;
        // Print the modified array
        foreach (var item in array)
        {
            Console.WriteLine(item);
        }
    }
}
using System;
class Program
{
    void Main()
    {
        int[] array = { 1, 2, 3, 4, 5 };
        // Create a span that points to the entire array
        Span<int> span = array;
        // Modify the data using the span
        span[2] = 10;
        // Print the modified array
        foreach (var item in array)
        {
            Console.WriteLine(item);
        }
    }
}
Imports System
Friend Class Program
	Private Sub Main()
		Dim array() As Integer = { 1, 2, 3, 4, 5 }
		' Create a span that points to the entire array
		Dim span As Span(Of Integer) = array
		' Modify the data using the span
		span(2) = 10
		' Print the modified array
		For Each item In array
			Console.WriteLine(item)
		Next item
	End Sub
End Class
$vbLabelText   $csharpLabel

ReadOnlySpan

Mientras que Spanes mutable y permite modificaciones en los datos subyacentes, ReadOnlySpanes una vista inmutable de la memoria. Proporciona una interfaz de sólo lectura a una región contigua de memoria, lo que la hace adecuada para escenarios en los que sólo se necesita leer los datos sin modificarlos.

He aquí algunos puntos clave.

1. Vista de sólo lectura

Como su nombre lo indica, ReadOnlySpanle 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<T>.

2. Representación de la memoria

Al igual que Span<T>, ReadOnlySpan<T> no posee la memoria a la que apunta. Se refiere a la memoria existente y puede apuntar a matrices, memoria asignada a pilas o memoria nativa.

3. Beneficios de rendimiento

Al igual que Span<T>, ReadOnlySpan<T> puede ofrecer un mejor rendimiento en comparación con los tipos de colección tradicionales, especialmente cuando se trata de grandes cantidades de datos, ya que reduce la necesidad de copiar.

4. No Bounds Checking

Al igual que Span, ReadOnlySpanno realiza comprobación de límites. Es responsabilidad del desarrollador asegurarse de que las operaciones se mantienen dentro de los límites de la memoria subyacente.

5. Utilización con Array Slicing

ReadOnlySpanadmite corte, lo que le permite crear sub-especies que hacen referencia a una parte 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;
    }
}
Imports System
Friend Class Program
	Shared Sub Main()
		Dim array() As Integer = { 1, 2, 3, 4, 5 }
		' Create a read-only span that points to the entire array
		Dim readOnlySpan As ReadOnlySpan(Of Integer) = array
		' Access and print the data through the read-only span
		For Each item In readOnlySpan
			Console.WriteLine(item)
		Next item
		' Note: The following line would result in a compilation error since readOnlySpan is read-only.
		' readOnlySpan[2] = 10;
	End Sub
End Class
$vbLabelText   $csharpLabel

Hay muchas maneras diferentes de crear ReadOnlySpan y trabajar con él. A continuación figuran algunos ejemplos.

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: H
string msg = "Hello, World!";
ReadOnlySpan<char> span1 = msg.AsSpan();
// Read-only manipulation
char firstChar = span1[0];
Console.WriteLine(firstChar); // Outputs: H
Dim msg As String = "Hello, World!"
Dim span1 As ReadOnlySpan(Of Char) = msg.AsSpan()
' Read-only manipulation
Dim firstChar As Char = span1(0)
Console.WriteLine(firstChar) ' Outputs: H
$vbLabelText   $csharpLabel

2. Trabajar con subcadenas

Usa Slice en el ReadOnlySpan<char>

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

3. Pasar una subcadena a un método

Pasa ReadOnlySpancomo un parámetro al método.

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

4. Búsqueda dentro de una cadena

ReadOnlySpanpara buscar dentro de una cadena con IndexOf().

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

5. Uso de archivos mapeados en memoria

ReadOnlySpanpuede ser más eficiente con archivos mapeados en memoria.

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

6. Manipulación eficiente de cadenas

ReadOnlySpanse puede usar para una manipulación de cadenas eficiente.

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

7. Pasar subcadena a las API

Cuando se trabaja con bibliotecas externas o API que operan con intervalos de caracteres.

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

ReadOnlySpanproporciona una manera de trabajar con cadenas de manera más eficiente, especialmente en escenarios donde las asignaciones de memoria y la copia deben minimizarse. Se trata de una potente herramienta para optimizar el rendimiento del código crítico y puede resultar especialmente beneficiosa cuando se trabaja con grandes cantidades de datos de cadenas.

Limitaciones de amplitud

Aunque Span en C# es una potente función con numerosas ventajas, conlleva ciertas limitaciones y consideraciones, especialmente en el contexto de la memoria contigua y no contigua. Exploremos estas limitaciones:

1. Memoria intermedia contigua

1.1 Sin gestión automática de la memoria

Spanno gestiona la memoria a la que apunta. Esto significa que si la memoria administrada subyacente se libera o sale del ámbito, usar Spanresultará en un comportamiento indefinido o posibles fallos. Los desarrolladores deben asegurarse de que la memoria subyacente siga siendo válida al usar un Span<T>.

1.2 Sin recogida de basura

Dado que Spanno posee memoria, no está sujeto 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 propio Span.

1.3 Comprobación de límites desactivada

Spany ReadOnlySpanno realizan verificación de límites por defecto. Esto puede llevar a acceder a posiciones de memoria no válidas si no se utiliza con cuidado. Los desarrolladores deben asegurarse manualmente de que las operaciones en un Spanse mantengan dentro de los límites de la memoria subyacente.

1.4 Sin soporte para memoria no contigua

Spanestá diseñado para trabajar con memoria contigua. Si tienes memoria no contigua o necesitas representar estructuras de datos más complejas, Spanpuede no ser la opción más adecuada.

1.5 No todas las operaciones son compatibles

Aunque Span<T> admite muchas operaciones comunes como segmentación, indexación e iteración, no todas las operaciones están admitidas. Por ejemplo, no se puede redimensionar 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<T> es parte de .NET Standard y .NET Core, puede que no esté disponible en todas las plataformas o entornos. Es crucial asegurarse de que su plataforma objetivo sea compatible con Spansi planea usarlo en su código.

2. Búferes de memoria no contiguos

2.1 Soporte limitado para memoria no contigua

ReadOnlySpan<T> está diseñado principalmente para trabajar sin problemas con bloques de memoria o búferes contiguos. Puede que no sea la opción más adecuada para escenarios en los que intervienen memorias intermedias no contiguas o estructuras con huecos en la memoria.

2.2 Limitaciones estructurales

Determinadas estructuras de datos o escenarios que dependen de memoria no contigua pueden no alinearse bien con ReadOnlySpan<T>. Por ejemplo, las estructuras de datos como las listas enlazadas o las estructuras de grafos pueden no ser adecuadas debido al requisito de memoria contigua de ReadOnlySpan<T>.

2.3 Operaciones complejas con punteros

En situaciones que involucran memoria no contigua, especialmente aquellas que requieren aritmética de punteros compleja, ReadOnlySpanpodría no ofrecer el mismo control de bajo nivel y flexibilidad que los punteros crudos en lenguajes como C++. En tales casos, utilizar código inseguro con punteros podría ser más apropiado.

2.4 Falta de soporte directo en algunas API

Similar al uso de memoria contigua, es importante señalar que no todas las API o bibliotecas pueden admitir directamente la memoria no contigua representada por ReadOnlySpan. La adaptación a este tipo de escenarios puede requerir pasos intermedios adicionales o conversiones para garantizar la compatibilidad.

Span y memoria no gestionada

En C#, Span puede utilizarse eficazmente con memoria no gestionada para realizar operaciones relacionadas con la memoria de forma controlada y eficiente. La memoria no gestionada se refiere a la memoria que no está gestionada por el recolector de basura del tiempo de ejecución de .NET, y a menudo implica el uso de asignaciones y desasignaciones de memoria nativa. He aquí cómo se puede utilizar Span con memoria no gestionada en C#.

Asignación de memoria no gestionada

Para asignar memoria no gestionada, puede utilizar la clase System.Runtime.InteropServices.MemoryMarshal. El método Marshal.AllocHGlobal asigna memoria y devuelve un puntero al bloque asignado. La memoria asignada o dirección de memoria se mantiene en un puntero unmanagedMemory y tendrá acceso de lectura-escritura. Se puede acceder fácilmente a las regiones contiguas de la memoria.

using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        const int bufferSize = 100;
        IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
        // Create a Span from the unmanaged memory
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        // Use the Span as needed...
        // Don't forget to free the unmanaged memory when done
        Marshal.FreeHGlobal(unmanagedMemory);
    }
}
using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        const int bufferSize = 100;
        IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
        // Create a Span from the unmanaged memory
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        // Use the Span as needed...
        // Don't forget to free the unmanaged memory when done
        Marshal.FreeHGlobal(unmanagedMemory);
    }
}
Imports System
Imports System.Runtime.InteropServices
Friend Class Program
	Shared Sub Main()
		Const bufferSize As Integer = 100
		Dim unmanagedMemory As IntPtr = Marshal.AllocHGlobal(bufferSize)
		' Create a Span from the unmanaged memory
		Dim span As New Span(Of Byte)(unmanagedMemory.ToPointer(), bufferSize)
		' Use the Span as needed...
		' Don't forget to free the unmanaged memory when done
		Marshal.FreeHGlobal(unmanagedMemory)
	End Sub
End Class
$vbLabelText   $csharpLabel

En el código fuente anterior, asignamos un bloque de memoria no administrada utilizando Marshal.AllocHGlobal y luego creamos un Spanutilizando el puntero obtenido de la memoria no administrada. Esto nos permite trabajar con memoria no gestionada utilizando la conocida API Span. Es importante tener en cuenta que cuando se trabaja con memoria no gestionada, usted es responsable de la gestión de la asignación y desasignación de la memoria.

Copia de datos desde y hacia la memoria no gestionada

Span proporciona métodos como Slice, CopyTo y ToArray que pueden utilizarse para copiar datos entre memoria gestionada y no gestionada de forma 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 = MemoryMarshal.Allocate<int>(sourceArray.Length);
        try
        {
            // Create a Span<int> from the source array
            Span<int> sourceSpan = sourceArray;
            // Create a Span<int> from the allocated unmanaged memory
            Span<int> destinationSpan = MemoryMarshal.Cast<int, byte>(destinationPointer, sourceArray.Length);
            // Copy data from the source Span<int> to the destination Span<int>
            sourceSpan.CopyTo(destinationSpan);
            // Print the values in the destination memory
            Console.WriteLine("Values in the destination memory:");
            foreach (var value in destinationSpan)
            {
                Console.Write($"{value} ");
            }
        }
        finally
        {
            // Deallocate the unmanaged memory when done
            MemoryMarshal.Free(destinationPointer);
        }
    }
}
using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        // Managed array to copy data from
        int[] sourceArray = { 1, 2, 3, 4, 5 };
        // Allocate unmanaged memory for the destination data
        IntPtr destinationPointer = MemoryMarshal.Allocate<int>(sourceArray.Length);
        try
        {
            // Create a Span<int> from the source array
            Span<int> sourceSpan = sourceArray;
            // Create a Span<int> from the allocated unmanaged memory
            Span<int> destinationSpan = MemoryMarshal.Cast<int, byte>(destinationPointer, sourceArray.Length);
            // Copy data from the source Span<int> to the destination Span<int>
            sourceSpan.CopyTo(destinationSpan);
            // Print the values in the destination memory
            Console.WriteLine("Values in the destination memory:");
            foreach (var value in destinationSpan)
            {
                Console.Write($"{value} ");
            }
        }
        finally
        {
            // Deallocate the unmanaged memory when done
            MemoryMarshal.Free(destinationPointer);
        }
    }
}
Imports System
Imports System.Runtime.InteropServices
Friend Class Program
	Shared Sub Main()
		' Managed array to copy data from
		Dim sourceArray() As Integer = { 1, 2, 3, 4, 5 }
		' Allocate unmanaged memory for the destination data
		Dim destinationPointer As IntPtr = MemoryMarshal.Allocate(Of Integer)(sourceArray.Length)
		Try
			' Create a Span<int> from the source array
			Dim sourceSpan As Span(Of Integer) = sourceArray
			' Create a Span<int> from the allocated unmanaged memory
			Dim destinationSpan As Span(Of Integer) = MemoryMarshal.Cast(Of Integer, Byte)(destinationPointer, sourceArray.Length)
			' Copy data from the source Span<int> to the destination Span<int>
			sourceSpan.CopyTo(destinationSpan)
			' Print the values in the destination memory
			Console.WriteLine("Values in the destination memory:")
			For Each value In destinationSpan
				Console.Write($"{value} ")
			Next value
		Finally
			' Deallocate the unmanaged memory when done
			MemoryMarshal.Free(destinationPointer)
		End Try
	End Sub
End Class
$vbLabelText   $csharpLabel

En este ejemplo:

MemoryMarshal.Allocate(sourceArray.Length) asigna memoria no administrada para los datos de destino. MemoryMarshal.Cast<int, byte>(destinationPointer, sourceArray.Length) crea un Spana partir de la memoria no administrada asignada. El método sourceSpan.CopyTo(destinationSpan) copia los datos del arreglo gestionado a la memoria no gestionada. Los valores de la memoria de destino se imprimen para verificar la operación de copia. El método MemoryMarshal.Free(destinationPointer) se utiliza para desalojar la memoria no administrada cuando se ha terminado.

Uso de código inseguro

Cuando se trabaja con memoria no gestionada, también se puede utilizar código inseguro con punteros. En tales casos, puede obtener un puntero del Span utilizando el método Unsafe.AsPointer().

using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        const int bufferSize = 100;
        IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
        // Create a Span from the unmanaged memory
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        // Use unsafe code to work with pointers
        // ref t
        unsafe
        {
            byte* pointer = (byte*)Unsafe.AsPointer(ref struct MemoryMarshal.GetReference(span));
            // Use the pointer as needed...
        }
        // Don't forget to free the unmanaged memory when done
        Marshal.FreeHGlobal(unmanagedMemory);
    }
}
using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        const int bufferSize = 100;
        IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
        // Create a Span from the unmanaged memory
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        // Use unsafe code to work with pointers
        // ref t
        unsafe
        {
            byte* pointer = (byte*)Unsafe.AsPointer(ref struct MemoryMarshal.GetReference(span));
            // Use the pointer as needed...
        }
        // Don't forget to free the unmanaged memory when done
        Marshal.FreeHGlobal(unmanagedMemory);
    }
}
Imports System
Imports System.Runtime.InteropServices
Friend Class Program
	Shared Sub Main()
		Const bufferSize As Integer = 100
		Dim unmanagedMemory As IntPtr = Marshal.AllocHGlobal(bufferSize)
		' Create a Span from the unmanaged memory
		Dim span As New Span(Of Byte)(unmanagedMemory.ToPointer(), bufferSize)
		' Use unsafe code to work with pointers
		' ref t
'INSTANT VB TODO TASK: C# 'unsafe' code is not converted by Instant VB:
'		unsafe
'		{
'			byte* pointer = (byte*)Unsafe.AsPointer(ref struct MemoryMarshal.GetReference(span));
'			' Use the pointer as needed...
'		}
		' Don't forget to free the unmanaged memory when done
		Marshal.FreeHGlobal(unmanagedMemory)
	End Sub
End Class
$vbLabelText   $csharpLabel

En este ejemplo, utilizamos el método Unsafe.AsPointer para obtener un puntero del Span. Esto nos permite utilizar código inseguro cuando trabajamos directamente con punteros.

Recuerde que, cuando se trabaja con memoria no gestionada, es crucial gestionar la asignación y desasignación correctamente para evitar fugas de memoria. Siempre libere la memoria no gestionada utilizando métodos apropiados, como Marshal.FreeHGlobal(). Además, tenga cuidado al utilizar código no seguro, ya que puede introducir posibles riesgos de seguridad si no se maneja adecuadamente.

Span y llamadas a métodos asíncronos

El uso de Span junto con llamadas a métodos asíncronos en C# es una potente combinación, especialmente cuando se trata de grandes cantidades de datos u operaciones de E/S. El objetivo es manejar las operaciones asíncronas sin copias innecesarias de datos de forma eficiente. Exploremos cómo puede aprovechar Span en escenarios asíncronos:

Operaciones de E/S asíncronas:

Al tratar con operaciones de E/S asíncronas, como leer o escribir datos a un flujo, puedes usar Memoryo Spanpara trabajar de manera eficiente con los datos sin crear buffers adicionales.

async Task ProcessDataAsync(Stream stream)
{
    const int bufferSize = 4096;
    byte[] buffer = new byte[bufferSize];
    while (true)
    {
        int bytesRead = await stream.ReadAsync(buffer.AsMemory());
        if (bytesRead == 0)
            break;
        // Process the data using Span without unnecessary copying
        ProcessData(buffer.AsSpan(0, bytesRead));
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
async Task ProcessDataAsync(Stream stream)
{
    const int bufferSize = 4096;
    byte[] buffer = new byte[bufferSize];
    while (true)
    {
        int bytesRead = await stream.ReadAsync(buffer.AsMemory());
        if (bytesRead == 0)
            break;
        // Process the data using Span without unnecessary copying
        ProcessData(buffer.AsSpan(0, bytesRead));
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
Async Function ProcessDataAsync(ByVal stream As Stream) As Task
	Const bufferSize As Integer = 4096
	Dim buffer(bufferSize - 1) As Byte
	Do
		Dim bytesRead As Integer = Await stream.ReadAsync(buffer.AsMemory())
		If bytesRead = 0 Then
			Exit Do
		End If
		' Process the data using Span without unnecessary copying
		ProcessData(buffer.AsSpan(0, bytesRead))
	Loop
End Function
Private Sub ProcessData(ByVal data As Span(Of Byte))
	' Perform operations on the data
End Sub
$vbLabelText   $csharpLabel

En este ejemplo, el método ReadAsync lee asíncronamente datos de un flujo en el búfer. El método ProcessData luego procesa los datos directamente desde el Spansin copiarlos a otro búfer.

2. Operaciones de Archivo Asincrónicas:

De forma similar a las operaciones de E/S, cuando se trata de operaciones de archivo asíncronas, puede utilizar Span para procesar datos de forma eficiente sin necesidad de realizar copias adicionales.

async Task ProcessFileAsync(string filePath)
{
    const int bufferSize = 4096;
    using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
    {
        byte[] buffer = new byte[bufferSize];
        while (true)
        {
            int bytesRead = await fileStream.ReadAsync(buffer.AsMemory());
            if (bytesRead == 0)
                break;
            // Process the data using Span without unnecessary copying
            ProcessData(buffer.AsSpan(0, bytesRead));
        }
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
async Task ProcessFileAsync(string filePath)
{
    const int bufferSize = 4096;
    using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
    {
        byte[] buffer = new byte[bufferSize];
        while (true)
        {
            int bytesRead = await fileStream.ReadAsync(buffer.AsMemory());
            if (bytesRead == 0)
                break;
            // Process the data using Span without unnecessary copying
            ProcessData(buffer.AsSpan(0, bytesRead));
        }
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
Async Function ProcessFileAsync(ByVal filePath As String) As Task
	Const bufferSize As Integer = 4096
	Using fileStream As New FileStream(filePath, FileMode.Open, FileAccess.Read)
		Dim buffer(bufferSize - 1) As Byte
		Do
			Dim bytesRead As Integer = Await fileStream.ReadAsync(buffer.AsMemory())
			If bytesRead = 0 Then
				Exit Do
			End If
			' Process the data using Span without unnecessary copying
			ProcessData(buffer.AsSpan(0, bytesRead))
		Loop
	End Using
End Function
Private Sub ProcessData(ByVal data As Span(Of Byte))
	' Perform operations on the data
End Sub
$vbLabelText   $csharpLabel

Aquí, el método ReadAsync lee datos de un flujo de archivo en el búfer, y el método ProcessData procesa los datos directamente desde el Span<byte>.

Procesamiento de tareas asíncronas:

Cuando trabajas con tareas asincrónicas que producen o consumen datos, puedes usar Memory<T> o Span<T> para evitar copias innecesarias.

async Task<int> ProcessDataAsync(int[] data)
{
    // Asynchronous processing of data
    await Task.Delay(1000);
    // Returning the length of the processed data
    return data.Length;
}
async Task Main()
{
    int[] inputData = Enumerable.Range(1, 1000).ToArray();
    // Process the data asynchronously without copying
    int processedLength = await ProcessDataAsync(inputData.AsMemory());
    Console.WriteLine($"Processed data length: {processedLength}");
}
async Task<int> ProcessDataAsync(int[] data)
{
    // Asynchronous processing of data
    await Task.Delay(1000);
    // Returning the length of the processed data
    return data.Length;
}
async Task Main()
{
    int[] inputData = Enumerable.Range(1, 1000).ToArray();
    // Process the data asynchronously without copying
    int processedLength = await ProcessDataAsync(inputData.AsMemory());
    Console.WriteLine($"Processed data length: {processedLength}");
}
Async Function ProcessDataAsync(ByVal data() As Integer) As Task(Of Integer)
	' Asynchronous processing of data
	Await Task.Delay(1000)
	' Returning the length of the processed data
	Return data.Length
End Function
Async Function Main() As Task
	Dim inputData() As Integer = Enumerable.Range(1, 1000).ToArray()
	' Process the data asynchronously without copying
	Dim processedLength As Integer = Await ProcessDataAsync(inputData.AsMemory())
	Console.WriteLine($"Processed data length: {processedLength}")
End Function
$vbLabelText   $csharpLabel

En este ejemplo, el método ProcessDataAsync procesa los datos de forma asíncrona y devuelve la longitud de los datos procesados sin necesidad de copias adicionales.

Presentación de IronPDF

Descripción general de la biblioteca IronPDF es la última biblioteca PDF de C# de Iron Software que se puede utilizar para generar documentos PDF hermosos de manera dinámica en el momento usando código C#. IronPDF ofrece diversas funciones, como la generación de PDF a partir de HTML, la conversión de contenido HTML a PDF, la fusión o división de archivos PDF, etc.

La característica principal de IronPDF es su funcionalidad de HTML a PDF, que preserva los diseños y estilos. Puede generar archivos PDF a partir de contenido web, por lo que es ideal para informes, facturas y documentación. Esta herramienta permite convertir archivos HTML, URL y cadenas HTML en 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");
    }
}
Imports IronPdf

Friend Class Program
	Shared Sub Main(ByVal args() As String)
		Dim renderer = New ChromePdfRenderer()

		' 1. Convert HTML String to PDF
		Dim htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>"
		Dim pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent)
		pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf")

		' 2. Convert HTML File to PDF
		Dim htmlFilePath = "path_to_your_html_file.html" ' Specify the path to your HTML file
		Dim pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath)
		pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf")

		' 3. Convert URL to PDF
		Dim url = "http://ironpdf.com" ' Specify the URL
		Dim pdfFromUrl = renderer.RenderUrlAsPdf(url)
		pdfFromUrl.SaveAs("URLToPDF.pdf")
	End Sub
End Class
$vbLabelText   $csharpLabel

Instalación

IronPDF se puede instalar usando 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
dotnet add package IronPdf
// Or
Install-Package IronPdf
'INSTANT VB TODO TASK: The following line uses invalid syntax:
'dotnet add package IronPdf Install-Package IronPdf
$vbLabelText   $csharpLabel

C# Span (Cómo Funciona para Desarrolladores): Figura 1 - Instala IronPDF usando el Gestor de Paquetes NuGet buscando ironpdf en la barra de búsqueda del Gestor de Paquetes NuGet

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

En este ejemplo, estamos utilizando Span junto con IronPDF para generar un documento PDF.

Salida:

C# Span (Cómo Funciona para Desarrolladores): Figura 2 - Salida de Consola

PDF Generado:

C# Span (Cómo Funciona para Desarrolladores): Figura 3 - Salida PDF

Licencias (prueba gratuita disponible)

Información sobre la licencia de IronPDF. Esta clave debe colocarse en appsettings.json.

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

Proporcione su correo electrónico para obtener una licencia de prueba.

Conclusión

Span<T> en C# ofrece una manera poderosa y eficiente de trabajar con la memoria, brindando beneficios en términos de rendimiento y flexibilidad. Su naturaleza no propietaria y contigua la hace especialmente adecuada para escenarios en los que es crucial minimizar las asignaciones y copias de memoria. Gracias a Span, los desarrolladores pueden mejorar el rendimiento de diversas aplicaciones, 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 Spanpara diversas tareas de manipulación de memoria de manera segura y eficiente. Junto con la visión general de la biblioteca de IronPDF, se puede utilizar para generar impresionantes documentos PDF sin límites de await y yield.

Por favor visita la página de Documentación de Inicio Rápido de IronPDF.

Chipego
Ingeniero de software
Chipego tiene una habilidad natural para escuchar que le ayuda a comprender los problemas de los clientes y a ofrecer soluciones inteligentes. Se unió al equipo de Iron Software en 2023, después de estudiar una licenciatura en Tecnología de la Información. IronPDF e IronOCR son los dos productos en los que Chipego se ha centrado, pero su conocimiento de todos los productos crece día a día, a medida que encuentra nuevas formas de ayudar a los clientes. Disfruta de lo colaborativa que es la vida en Iron Software, con miembros del equipo de toda la empresa que aportan su variada experiencia para contribuir a soluciones eficaces e innovadoras. Cuando Chipego está lejos de su escritorio, a menudo se le puede encontrar disfrutando de un buen libro o jugando al fútbol.
< ANTERIOR
C# IDE (Cómo funciona para los desarrolladores)
SIGUIENTE >
Opentelemetry C# (Cómo funciona para desarrolladores)