AYUDA .NET

C# Span (Cómo funciona para desarrolladores)

Actualizado 6 de marzo, 2024
Compartir:

Span es un tipo introducido en C# 7.2 como parte del Spanstruct en el espacio de nombres System. Está diseñado para representar una región contigua de memoria arbitraria. A diferencia de los arrays o colecciones como managed heap, Span no posee la memoria de la pila o 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 IronPDF biblioteca 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 };
        //  Crear un span que apunte a todo el array
        Span<int> span = array;
        //  Modificar los datos utilizando el span
        span [2] = 10;
        //  Imprime la matriz modificada
        foreach (var item in array)
        {
            Console.WriteLine(item);
        }
    }
}
using System;
class Program
{
    void Main()
    {
        int [] array = { 1, 2, 3, 4, 5 };
        //  Crear un span que apunte a todo el array
        Span<int> span = array;
        //  Modificar los datos utilizando el span
        span [2] = 10;
        //  Imprime la matriz modificada
        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 }
		'  Crear un span que apunte a todo el array
		Dim span As Span(Of Integer) = array
		'  Modificar los datos utilizando el span
		span (2) = 10
		'  Imprime la matriz modificada
		For Each item In array
			Console.WriteLine(item)
		Next item
	End Sub
End Class
VB   C#

ReadOnlySpan

Mientras Spanes mutable y permite modificar 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 indica, ReadOnlySpanpermite crear una vista de sólo lectura de un bloque de memoria. Esto significa que no se pueden modificar los elementos a través de un ReadOnlySpan.

2. Representación de la memoria

Como Span, ReadOnlySpanno es propietaria de 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

Como Span, ReadOnlySpanpuede ofrecer un mejor rendimiento en comparación con los tipos de recogida tradicionales, especialmente cuando se trata de grandes cantidades de datos, ya que reduce la necesidad de realizar copias.

4. No Bounds Checking

Al igual que con Span, ReadOnlySpanno realiza la 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 el troceado, lo que permite crear subámbitos 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 };
        //  Crear un span de sólo lectura que apunte a todo el array
        ReadOnlySpan<int> readOnlySpan = array;
        //  Acceder e imprimir los datos a través del tramo de sólo lectura
        foreach (var item in readOnlySpan)
        {
            Console.WriteLine(item);
        }
        //  Nota: La siguiente línea daría lugar a un error de compilación ya que readOnlySpan es de sólo lectura.
        //  readOnlySpan [2] = 10;
    }
}
using System;
class Program
{
    static void Main()
    {
        int [] array = { 1, 2, 3, 4, 5 };
        //  Crear un span de sólo lectura que apunte a todo el array
        ReadOnlySpan<int> readOnlySpan = array;
        //  Acceder e imprimir los datos a través del tramo de sólo lectura
        foreach (var item in readOnlySpan)
        {
            Console.WriteLine(item);
        }
        //  Nota: La siguiente línea daría lugar a un error de compilación ya que readOnlySpan es de sólo lectura.
        //  readOnlySpan [2] = 10;
    }
}
Imports System
Friend Class Program
	Shared Sub Main()
		Dim array() As Integer = { 1, 2, 3, 4, 5 }
		'  Crear un span de sólo lectura que apunte a todo el array
		Dim readOnlySpan As ReadOnlySpan(Of Integer) = array
		'  Acceder e imprimir los datos a través del tramo de sólo lectura
		For Each item In readOnlySpan
			Console.WriteLine(item)
		Next item
		'  Nota: La siguiente línea daría lugar a un error de compilación ya que readOnlySpan es de sólo lectura.
		'  readOnlySpan [2] = 10;
	End Sub
End Class
VB   C#

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();
//  Manipulación de sólo lectura
char firstChar = span1 [0];
Console.WriteLine(firstChar); //  Salidas: H
string msg = "Hello, World!";
ReadOnlySpan<char> span1 = msg.AsSpan();
//  Manipulación de sólo lectura
char firstChar = span1 [0];
Console.WriteLine(firstChar); //  Salidas: H
Dim msg As String = "Hello, World!"
Dim span1 As ReadOnlySpan(Of Char) = msg.AsSpan()
'  Manipulación de sólo lectura
Dim firstChar As Char = span1 (0)
Console.WriteLine(firstChar) '  Salidas: H
VB   C#

2. Trabajar con subcadenas

Utilizar Slice en el ReadOnlySpan

ReadOnlySpan<char> substringSpan = spanFromString.Slice(startIndex, length);
ReadOnlySpan<char> substringSpan = spanFromString.Slice(startIndex, length);
Dim substringSpan As ReadOnlySpan(Of Char) = spanFromString.Slice(startIndex, length)
VB   C#

3. Pasar una subcadena a un método

Pasar ReadOnlySpancomo parámetro del método.

void ProcessSubstringfromReadOnlySpan(ReadOnlySpan<char> substring)
{
    //  Realizar operaciones en la subcadena
}
//  Utilización
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(startIndex, length));
void ProcessSubstringfromReadOnlySpan(ReadOnlySpan<char> substring)
{
    //  Realizar operaciones en la subcadena
}
//  Utilización
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(startIndex, length));
Private Sub ProcessSubstringfromReadOnlySpan(ByVal substring As ReadOnlySpan(Of Char))
	'  Realizar operaciones en la subcadena
End Sub
'  Utilización
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(startIndex, length))
VB   C#

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)
VB   C#

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);
        //  Procesar datos directamente desde el archivo mapeado en memoria
        ProcessData(dataSpan);
    }
}
using (var memmf = MemoryMappedFile.CreateFromFile("data.bin"))
{
    using (var accessor = memmf.CreateViewAccessor())
    {
        ReadOnlySpan<byte> dataSpan;
        accessor.Read(0, out dataSpan);
        //  Procesar datos directamente desde el archivo mapeado en memoria
        ProcessData(dataSpan);
    }
}
Using memmf = MemoryMappedFile.CreateFromFile("data.bin")
	Using accessor = memmf.CreateViewAccessor()
		Dim dataSpan As ReadOnlySpan(Of Byte) = Nothing
		accessor.Read(0, dataSpan)
		'  Procesar datos directamente desde el archivo mapeado en memoria
		ProcessData(dataSpan)
	End Using
End Using
VB   C#

6. Manipulación eficiente de cadenas

ReadOnlySpanpuede utilizarse para una manipulación eficaz de las cadenas.

//  Sustituir un carácter de una subcadena sin crear una nueva cadena
spanFromString.Slice(startIndex, length).CopyTo(newSpan);
//  Sustituir un carácter de una subcadena sin crear una nueva cadena
spanFromString.Slice(startIndex, length).CopyTo(newSpan);
'  Sustituir un carácter de una subcadena sin crear una nueva cadena
spanFromString.Slice(startIndex, length).CopyTo(newSpan)
VB   C#

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)
{
    //  Llamar a la API externa con el carácter span
}
//  Utilización
ExternalApiMethod(spanFromString.Slice(startIndex, length));
void ExternalApiMethod(ReadOnlySpan<char> data)
{
    //  Llamar a la API externa con el carácter span
}
//  Utilización
ExternalApiMethod(spanFromString.Slice(startIndex, length));
Private Sub ExternalApiMethod(ByVal data As ReadOnlySpan(Of Char))
	'  Llamar a la API externa con el carácter span
End Sub
'  Utilización
ExternalApiMethod(spanFromString.Slice(startIndex, length))
VB   C#

ReadOnlySpanproporciona una forma de trabajar con cadenas de forma más eficiente, especialmente en escenarios en los que las asignaciones de memoria y las copias 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 gestionada subyacente se libera o sale de ámbito, el uso de la función Spanprovocará un comportamiento indefinido o posibles fallos. Los desarrolladores deben asegurarse de que la memoria subyacente sigue siendo válida cuando se utiliza un Span.

1.2 Sin recogida de basura

Desde Spanno posee memoria, no está sujeta a la recolección de basura. Por lo tanto, debe tener cuidado cuando trabaje con memoria asignada a pilas o memoria que tenga un tiempo de vida más corto que la memoria Spansí mismo.

1.3 Comprobación de límites desactivada

Spany ReadOnlySpanno realizan la comprobació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 Spanpermanecer 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 dispone de memoria no contigua o necesita representar estructuras de datos más complejas, Spanpuede no ser la opción más adecuada.

1.5 No todas las operaciones son compatibles

Mientras Spanadmite muchas operaciones comunes como trocear, indexar e iterar, pero no todas las operaciones son compatibles. Por ejemplo, no se puede cambiar el tamaño de un Spany ciertas operaciones que implican cambiar la longitud de la memoria subyacente no están permitidas.

1.6 Compatibilidad limitada de plataformas

Mientras Spanforma parte de .NET Standard y .NET Core, es posible que no esté disponible en todas las plataformas o entornos. Es crucial asegurarse de que la plataforma de destino es compatible con Spansi piensa utilizarlo en su código.

2. Búferes de memoria no contiguos

2.1 Soporte limitado para memoria no contigua

ReadOnlySpanestá diseñado principalmente para trabajar sin problemas con bloques de memoria contiguos o buffers. 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

Ciertas estructuras de datos o escenarios que dependen de memoria no contigua pueden no alinearse bien con ReadOnlySpan. Por ejemplo, las estructuras de datos como las listas enlazadas o las estructuras de grafos podrían no ser adecuadas debido a los requisitos de memoria contigua de ReadOnlySpan.

2.3 Operaciones complejas con punteros

En situaciones que implican memoria no contigua, particularmente aquellas que requieren aritmética de punteros compleja, ReadOnlySpanpuede no ofrecer el mismo control y flexibilidad de bajo nivel que los punteros sin formato 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

Al igual que ocurre con la memoria contigua, es importante tener en cuenta que no todas las API o bibliotecas admiten 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);
        //  Crear un Span a partir de la memoria no gestionada
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        //  Utiliza el Span cuando sea necesario...
        //  No olvides liberar la memoria no gestionada cuando termines
        Marshal.FreeHGlobal(unmanagedMemory);
    }
}
using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        const int bufferSize = 100;
        IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
        //  Crear un Span a partir de la memoria no gestionada
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        //  Utiliza el Span cuando sea necesario...
        //  No olvides liberar la memoria no gestionada cuando termines
        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)
		'  Crear un Span a partir de la memoria no gestionada
		Dim span As New Span(Of Byte)(unmanagedMemory.ToPointer(), bufferSize)
		'  Utiliza el Span cuando sea necesario...
		'  No olvides liberar la memoria no gestionada cuando termines
		Marshal.FreeHGlobal(unmanagedMemory)
	End Sub
End Class
VB   C#

En el código fuente anterior, asignamos un bloque de memoria no gestionada utilizando Marshal.AllocHGlobal y luego creamos un Spanutilizando el puntero obtenido de la memoria no gestionada. 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()
    {
        //  Matriz gestionada de la que copiar datos
        int [] sourceArray = { 1, 2, 3, 4, 5 };
        //  Asignar memoria no gestionada para los datos de destino
        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);
            //  Imprimir los valores de la memoria de destino
            Console.WriteLine("Values in the destination memory:");
            foreach (var value in destinationSpan)
            {
                Console.Write($"{value} ");
            }
        }
        finally
        {
            //  Desasignar la memoria no gestionada cuando haya terminado
            MemoryMarshal.Free(destinationPointer);
        }
    }
}
using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        //  Matriz gestionada de la que copiar datos
        int [] sourceArray = { 1, 2, 3, 4, 5 };
        //  Asignar memoria no gestionada para los datos de destino
        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);
            //  Imprimir los valores de la memoria de destino
            Console.WriteLine("Values in the destination memory:");
            foreach (var value in destinationSpan)
            {
                Console.Write($"{value} ");
            }
        }
        finally
        {
            //  Desasignar la memoria no gestionada cuando haya terminado
            MemoryMarshal.Free(destinationPointer);
        }
    }
}
Imports System
Imports System.Runtime.InteropServices
Friend Class Program
	Shared Sub Main()
		'  Matriz gestionada de la que copiar datos
		Dim sourceArray() As Integer = { 1, 2, 3, 4, 5 }
		'  Asignar memoria no gestionada para los datos de destino
		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)
			'  Imprimir los valores de la memoria de destino
			Console.WriteLine("Values in the destination memory:")
			For Each value In destinationSpan
				Console.Write($"{value} ")
			Next value
		Finally
			'  Desasignar la memoria no gestionada cuando haya terminado
			MemoryMarshal.Free(destinationPointer)
		End Try
	End Sub
End Class
VB   C#

En este ejemplo:

MemoryMarshal.Allocate(sourceArray.Length) asigna memoria no gestionada para los datos de destino. MemoryMarshal.Cast<int, byte>(destinationPointer, sourceArray.Length) crea un Spande la memoria no gestionada asignada. El sourceSpan.CopyTo(destinationSpan) copia los datos de la matriz gestionada a la memoria no gestionada. Los valores de la memoria de destino se imprimen para verificar la operación de copia. La MemoriaMarshal.Free(destinationPointer) se utiliza para desasignar la memoria no gestionada una vez finalizado el proceso.

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() método.

using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        const int bufferSize = 100;
        IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
        //  Crear un Span a partir de la memoria no gestionada
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        //  Utilizar código inseguro para trabajar con punteros
        //  ref t
        unsafe
        {
            byte* pointer = (byte*)Unsafe.AsPointer(ref struct MemoryMarshal.GetReference(span));
            //  Utiliza el puntero según necesites...
        }
        //  No olvides liberar la memoria no gestionada cuando termines
        Marshal.FreeHGlobal(unmanagedMemory);
    }
}
using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        const int bufferSize = 100;
        IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
        //  Crear un Span a partir de la memoria no gestionada
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        //  Utilizar código inseguro para trabajar con punteros
        //  ref t
        unsafe
        {
            byte* pointer = (byte*)Unsafe.AsPointer(ref struct MemoryMarshal.GetReference(span));
            //  Utiliza el puntero según necesites...
        }
        //  No olvides liberar la memoria no gestionada cuando termines
        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)
		'  Crear un Span a partir de la memoria no gestionada
		Dim span As New Span(Of Byte)(unmanagedMemory.ToPointer(), bufferSize)
		'  Utilizar código inseguro para trabajar con punteros
		'  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));
'			'  Utiliza el puntero según necesites...
'		}
		'  No olvides liberar la memoria no gestionada cuando termines
		Marshal.FreeHGlobal(unmanagedMemory)
	End Sub
End Class
VB   C#

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. Libere siempre la memoria no gestionada utilizando métodos adecuados, 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:

1. Operaciones de E/S asíncronas:

Cuando se trata de operaciones de E/S asíncronas, como la lectura o escritura de datos en un flujo, puede utilizar Memoryo Spanpara trabajar eficazmente con los datos sin crear búferes 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;
        //  Procesar los datos mediante Span sin copias innecesarias
        ProcessData(buffer.AsSpan(0, bytesRead));
    }
}
void ProcessData(Span<byte> data)
{
    //  Realizar operaciones con los datos
}
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;
        //  Procesar los datos mediante Span sin copias innecesarias
        ProcessData(buffer.AsSpan(0, bytesRead));
    }
}
void ProcessData(Span<byte> data)
{
    //  Realizar operaciones con los datos
}
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
		'  Procesar los datos mediante Span sin copias innecesarias
		ProcessData(buffer.AsSpan(0, bytesRead))
	Loop
End Function
Private Sub ProcessData(ByVal data As Span(Of Byte))
	'  Realizar operaciones con los datos
End Sub
VB   C#

En este ejemplo, el método ReadAsync lee asíncronamente datos de un flujo en el búfer. A continuación, el método ProcessData procesa los datos directamente desde el Spansin copiarlo en otro búfer.

2. Operaciones asíncronas de archivos:

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;
            //  Procesar los datos mediante Span sin copias innecesarias
            ProcessData(buffer.AsSpan(0, bytesRead));
        }
    }
}
void ProcessData(Span<byte> data)
{
    //  Realizar operaciones con los datos
}
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;
            //  Procesar los datos mediante Span sin copias innecesarias
            ProcessData(buffer.AsSpan(0, bytesRead));
        }
    }
}
void ProcessData(Span<byte> data)
{
    //  Realizar operaciones con los datos
}
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
			'  Procesar los datos mediante Span sin copias innecesarias
			ProcessData(buffer.AsSpan(0, bytesRead))
		Loop
	End Using
End Function
Private Sub ProcessData(ByVal data As Span(Of Byte))
	'  Realizar operaciones con los datos
End Sub
VB   C#

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

3. Procesamiento de tareas asíncronas:

Cuando se trabaja con tareas asíncronas que producen o consumen datos, se puede utilizar Memoryo Spanpara evitar copias innecesarias.

async Task<int> ProcessDataAsync(int [] data)
{
    //  Tratamiento asíncrono de datos
    await Task.Delay(1000);
    //  Devuelve la longitud de los datos procesados
    return data.Length;
}
async Task Main()
{
    int [] inputData = Enumerable.Range(1, 1000).ToArray();
    //  Procesar los datos de forma asíncrona sin copiarlos
    int processedLength = await ProcessDataAsync(inputData.AsMemory());
    Console.WriteLine($"Processed data length: {processedLength}");
}
async Task<int> ProcessDataAsync(int [] data)
{
    //  Tratamiento asíncrono de datos
    await Task.Delay(1000);
    //  Devuelve la longitud de los datos procesados
    return data.Length;
}
async Task Main()
{
    int [] inputData = Enumerable.Range(1, 1000).ToArray();
    //  Procesar los datos de forma asíncrona sin copiarlos
    int processedLength = await ProcessDataAsync(inputData.AsMemory());
    Console.WriteLine($"Processed data length: {processedLength}");
}
Async Function ProcessDataAsync(ByVal data() As Integer) As Task(Of Integer)
	'  Tratamiento asíncrono de datos
	Await Task.Delay(1000)
	'  Devuelve la longitud de los datos procesados
	Return data.Length
End Function
Async Function Main() As Task
	Dim inputData() As Integer = Enumerable.Range(1, 1000).ToArray()
	'  Procesar los datos de forma asíncrona sin copiarlos
	Dim processedLength As Integer = Await ProcessDataAsync(inputData.AsMemory())
	Console.WriteLine($"Processed data length: {processedLength}")
End Function
VB   C#

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

IronPDF es la última biblioteca PDF en C# de Iron Software que puede utilizarse para generar hermosos documentos PDF sobre la marcha de forma dinámica utilizando 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.

Instalación

IronPDF puede instalarse mediante la aplicación NuGet Package Manager o utilizando el gestor de paquetes de Visual Studio.

dotnet add package IronPdf
//  O
Install-Package IronPdf
dotnet add package IronPdf
//  O
Install-Package IronPdf
'INSTANT VB TODO TASK: The following line uses invalid syntax:
'dotnet add package IronPdf Install-Package IronPdf
VB   C#

C# Span (Cómo funciona para desarrolladores): Figura 1 - Instale IronPDF utilizando NuGet Package Manager buscando "ironpdf" en la barra de búsqueda de NuGet Package Manager

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
VB   C#

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 la consola

PDF generado:

C# Span (Cómo funciona para desarrolladores): Figura 3 - Salida PDF

Licencias (prueba gratuita disponible)

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"
VB   C#

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

Conclusión

Spanen C# proporciona una forma potente y eficaz de trabajar con la memoria, ofreciendo ventajas 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. Comprendiendo sus características y teniendo en cuenta sus limitaciones, los desarrolladores pueden aprovechar Spanpara diversas tareas de manipulación de la memoria de forma segura y eficaz. Junto con IronPDF, se puede utilizar para generar impresionantes documentos PDF sin límites de espera y rendimiento.

Junto con el prueba gratuita para un uso prolongado. Para saber más sobre cómo utilizar IronPDF visite su documentación página.

< ANTERIOR
C# IDE (Cómo funciona para los desarrolladores)
SIGUIENTE >
Opentelemetry C# (Cómo funciona para desarrolladores)

¿Listo para empezar? Versión: 2024.6 recién publicada

Comenzar prueba gratuita Descargas totales: 9,602,420
Ver licencias >