Ir para o conteúdo do rodapé
AJUDA DO .NET

Span em C# (Como funciona para desenvolvedores)

Span é um tipo introduzido no C# 7.2 como parte da estrutura Span no namespace System. Ele foi projetado para representar uma região contígua de memória arbitrária. Ao contrário de arrays ou coleções como o heap gerenciado, o Span não possui a memória da pilha ou a região de memória para a qual aponta; Em vez disso, oferece uma visão simplificada dos blocos de memória existentes. Essa característica torna o Span particularmente poderoso para cenários em que você precisa trabalhar com buffers de memória de forma eficiente, sem incorrer em sobrecarga adicional e cenários de código inseguros. Mais adiante neste artigo, veremos também a introdução à biblioteca IronPDF da Iron Software .

Principais características do vão

1. Gerenciamento de memória

Em C#, o Span permite que os desenvolvedores trabalhem diretamente com a memória sem recorrer às alocações tradicionais de heap. Oferece uma maneira de criar fatias de memória a partir de arrays existentes ou outras fontes de memória, eliminando a necessidade de cópias de memória adicionais.

2. Abstrações de Cópia Zero

Uma das características mais marcantes do Span em C# é a sua abstração de cópia zero. Em vez de duplicar dados, o Span oferece uma maneira eficiente de referenciar a memória existente. Isso é particularmente benéfico em cenários onde copiar grandes quantidades de dados seria impraticável ou muito caro.

3. Operações do tipo ponteiro

Embora o C# tenha sido tradicionalmente uma linguagem segura e de alto nível, o Span introduz um grau de manipulação de memória de baixo nível semelhante ao trabalho com ponteiros em linguagens como C ou C++. Os desenvolvedores podem executar operações semelhantes a ponteiros sem sacrificar a segurança e a natureza gerenciada do C#.

4. Natureza Imutável

Apesar de suas capacidades de acesso à memória de baixo nível, o Span em C# permanece imutável. Isso significa que, embora permita a manipulação da memória, garante a segurança ao impedir modificações não intencionais.

Exemplo

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);
        }
    }
}
Imports System

Friend Class Program
	Shared 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

Somente leitura

Enquanto Span é mutável e permite modificações nos dados subjacentes, ReadOnlySpan é uma visão imutável da memória. Ela fornece uma interface somente leitura para uma região contígua de memória, tornando-a adequada para cenários em que você precisa apenas ler os dados sem modificá-los.

Aqui estão alguns pontos-chave.

1. Modo somente leitura

Como o nome sugere, ReadOnlySpan permite criar uma visão de leitura de um bloco de memória. Isso significa que você não pode modificar os elementos através de um ReadOnlySpan.

2. Representação da memória

Como Span, ReadOnlySpan não possui a memória para a qual aponta. Refere-se à memória existente e pode apontar para arrays, memória alocada na pilha ou memória nativa.

3. Benefícios de desempenho

Como Span, ReadOnlySpan pode oferecer melhor desempenho em comparação com tipos de coleção tradicionais, especialmente ao lidar com grandes volumes de dados, pois reduz a necessidade de cópias.

4. Sem verificação de limites

Assim como Span, ReadOnlySpan não realiza verificação de limites. É responsabilidade do desenvolvedor garantir que as operações permaneçam dentro dos limites da memória subjacente.

5. Utilização com fatiamento de matrizes

ReadOnlySpan suporta slicing, permitindo criar subspans que referenciam uma parte da memória original.

Exemplo

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

Existem muitas maneiras diferentes de criar um ReadOnlySpan e trabalhar com ele. Abaixo seguem alguns exemplos.

1. Criando ReadOnlySpan a partir de uma 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. Trabalhando com substrings

Use Slice on the ReadOnlySpan

// Example usage of Slice method on ReadOnlySpan<char>
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ReadOnlySpan<char> substringSpan = spanFromString.Slice(7, 6); // Extracts 'String'
// Example usage of Slice method on ReadOnlySpan<char>
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan();
ReadOnlySpan<char> substringSpan = spanFromString.Slice(7, 6); // Extracts 'String'
' Example usage of Slice method on ReadOnlySpan<char>
Dim spanFromString As ReadOnlySpan(Of Char) = "Sample String".AsSpan()
Dim substringSpan As ReadOnlySpan(Of Char) = spanFromString.Slice(7, 6) ' Extracts 'String'
$vbLabelText   $csharpLabel

3. Passando uma substring para um método

Pass ReadOnlySpan como um parâmetro para o método.

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));
Private Sub ProcessSubstringfromReadOnlySpan(ByVal substring As ReadOnlySpan(Of Char))
	' Perform operations on the substring
End Sub

' Usage
Private spanFromString As ReadOnlySpan(Of Char) = "Sample String".AsSpan()
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(7, 6))
$vbLabelText   $csharpLabel

4. Pesquisando dentro de uma string

ReadOnlySpan para pesquisar dentro de uma string com IndexOf().

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

5. Utilizando arquivos mapeados em memória

Somente leitura Pode ser mais eficiente com arquivos mapeados em memória.

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);
            }
        }
    }
}
Imports System
Imports System.IO.MemoryMappedFiles

Friend Class Program
	Private Shared Sub ProcessData(ByVal data As ReadOnlySpan(Of Byte))
		' Process data directly from the memory-mapped file
	End Sub

	Shared Sub Main()
		Using memmf = MemoryMappedFile.CreateFromFile("data.bin")
			Using accessor = memmf.CreateViewAccessor()
				Dim buffer(accessor.Capacity - 1) As Byte
				accessor.ReadArray(0, buffer, 0, buffer.Length)

				Dim dataSpan As New ReadOnlySpan(Of Byte)(buffer)
				ProcessData(dataSpan)
			End Using
		End Using
	End Sub
End Class
$vbLabelText   $csharpLabel

6. Manipulação eficiente de strings

Somente leitura Pode ser usado para manipulação eficiente de strings.

Span<char> newSpan = new char[6];
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan().Slice(7, 6);
spanFromString.CopyTo(newSpan);
Console.WriteLine(new string(newSpan)); // Outputs: String
Span<char> newSpan = new char[6];
ReadOnlySpan<char> spanFromString = "Sample String".AsSpan().Slice(7, 6);
spanFromString.CopyTo(newSpan);
Console.WriteLine(new string(newSpan)); // Outputs: String
Dim newSpan As Span(Of Char) = New Char(5){}
Dim spanFromString As ReadOnlySpan(Of Char) = "Sample String".AsSpan().Slice(7, 6)
spanFromString.CopyTo(newSpan)
Console.WriteLine(New String(newSpan)) ' Outputs: String
$vbLabelText   $csharpLabel

7. Passando substrings para APIs

Ao trabalhar com bibliotecas externas ou APIs que operam em intervalos 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));
Private Sub ExternalApiMethod(ByVal data As ReadOnlySpan(Of Char))
	' Call the external API with the character span
End Sub

' Usage
Private spanFromString As ReadOnlySpan(Of Char) = "Sample String".AsSpan()
ExternalApiMethod(spanFromString.Slice(7, 6))
$vbLabelText   $csharpLabel

Somente leitura Oferece uma maneira de trabalhar com strings de forma mais eficiente, especialmente em cenários onde a alocação e a cópia de memória devem ser minimizadas. É uma ferramenta poderosa para otimizar código crítico em termos de desempenho e pode ser particularmente benéfica ao lidar com grandes quantidades de dados em formato de string.

Limitações de extensão

Embora o Span em C# seja um recurso poderoso com inúmeras vantagens, ele apresenta certas limitações e considerações, principalmente no contexto de memória contígua e não contígua. Vamos explorar essas limitações:

1. Buffers de memória contígua

1.1 Sem gerenciamento automático de memória

Span não gerencia a memória para a qual aponta. Isso significa que se a memória gerenciada subjacente for liberada ou sair do escopo, o uso de Span resultará em comportamento indefinido ou possíveis falhas. Os desenvolvedores precisam garantir que a memória subjacente ainda seja válida ao usar um Span.

1.2 Sem coleta de lixo

Como Span não possui memória, ele não está sujeito à coleta de lixo. Portanto, é necessário cuidado ao trabalhar com memória alocada na pilha ou memória que tenha um tempo de vida mais curto que o próprio Span.

1.3 A verificação de limites está desativada.

Span e ReadOnlySpan não realizam verificação de limites por padrão. Isso pode levar ao acesso a locais de memória inválidos se não for usado com cuidado. Os desenvolvedores precisam garantir manualmente que as operações em um Span permaneçam dentro dos limites da memória subjacente.

1.4 Sem suporte para memória não contígua

Span é projetado para trabalhar com memória contígua. Se você tiver memória não contígua ou precisar representar estruturas de dados mais complexas, Span pode não ser a escolha mais apropriada.

1.5 Nem todas as operações são suportadas

Embora Span suporte muitas operações comuns como slicing, indexação e iteração, nem todas as operações são suportadas. Por exemplo, você não pode redimensionar um Span, e certas operações que envolvem a alteração do comprimento da memória subjacente não são permitidas.

1.6 Compatibilidade limitada com a plataforma

Embora Span faça parte do .NET Standard e .NET Core, pode não estar disponível em todas as plataformas ou ambientes. É crucial garantir que a sua plataforma de destino suporte Span se planeja usá-lo em seu código.

2. Buffers de memória não contíguos

2.1 Suporte limitado para memória não contígua

ReadOnlySpan é projetado principalmente para funcionar perfeitamente com blocos de memória contígua ou buffers. Pode não ser a opção mais adequada para cenários que envolvam buffers de memória não contíguos ou estruturas com lacunas de memória.

2.2 Limitações Estruturais

Certas estruturas de dados ou cenários que dependem de memória não contígua podem não alinhar bem com ReadOnlySpan. Por exemplo, estruturas de dados como listas encadeadas ou estruturas gráficas podem não ser adequadas devido à exigência de memória contígua do ReadOnlySpan.

2.3 Operações complexas com ponteiros

Em situações que envolvem memória não contígua, particularmente aquelas que requerem aritmética de ponteiro intricada, ReadOnlySpan pode não oferecer o mesmo controle e flexibilidade de baixo nível que os ponteiros brutos em linguagens como C++. Nesses casos, utilizar código inseguro com ponteiros pode ser mais apropriado.

2.4 Falta de suporte direto em algumas APIs

Semelhante à memória contígua, é importante notar que nem todas as APIs ou bibliotecas podem suportar diretamente memória não contígua representada por ReadOnlySpan. A adaptação a esses cenários pode exigir etapas intermediárias adicionais ou conversões para garantir a compatibilidade.

Span e memória não gerenciada

Em C#, o Span pode ser usado de forma eficaz com memória não gerenciada para executar operações relacionadas à memória de maneira controlada e eficiente. Memória não gerenciada refere-se à memória que não é gerenciada pelo coletor de lixo do ambiente de execução .NET e, frequentemente, envolve o uso de alocações e desalocações de memória nativa. Eis como o Span pode ser utilizado com memória não gerenciada em C#.

Alocando memória não gerenciada

Para alocar memória não gerenciada, você pode usar a classe System.Runtime.InteropServices.MemoryMarshal. O método Marshal.AllocHGlobal aloca memória e retorna um ponteiro para o bloco alocado. A memória alocada ou o endereço de memória é armazenado em um ponteiro unmanagedMemory e terá acesso de leitura e gravação. Regiões contíguas da memória podem ser acessadas facilmente.

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

No código fonte acima, alocamos um bloco de memória não gerenciada usando Marshal.AllocHGlobal e então criamos um Span<byte> usando o ponteiro obtido da memória não gerenciada. Isso nos permite trabalhar com memória não gerenciada usando a API Span, que já conhecemos. É importante observar que, ao trabalhar com memória não gerenciada, você é responsável por gerenciar a alocação e a desalocação da memória.

Copiando dados de e para memória não gerenciada

Span fornece métodos como Slice, CopyTo, e ToArray que podem ser usados para copiar dados entre memória gerenciada e não gerenciada 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 = 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);
        }
    }
}
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 = Marshal.AllocHGlobal(sourceArray.Length * Len(New Integer()))
		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 New Span(Of Integer)(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:")
			For Each value In destinationSpan
				Console.Write($"{value} ")
			Next value
		Finally
			' Deallocate the unmanaged memory when done
			Marshal.FreeHGlobal(destinationPointer)
		End Try
	End Sub
End Class
$vbLabelText   $csharpLabel

Neste exemplo:

  • Marshal.AllocHGlobal aloca memória não gerenciada para os dados de destino.
  • new Span<int>(destinationPointer.ToPointer(), sourceArray.Length) cria um Span<int> da memória não gerenciada alocada.
  • O método sourceSpan.CopyTo(destinationSpan) copia os dados da matriz gerenciada para a memória não gerenciada.
  • Os valores na memória de destino são impressos para verificar a operação de cópia.
  • O método Marshal.FreeHGlobal(destinationPointer) é usado para desalocar a memória não gerenciada quando terminado.

Utilizando código inseguro

Ao lidar com memória não gerenciada, você também pode usar código inseguro com ponteiros. Nesses casos, você pode obter um ponteiro do Span usando o 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);
    }
}
Imports System
Imports System.Runtime.InteropServices
Imports System.Runtime.CompilerServices

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
'INSTANT VB TODO TASK: C# 'unsafe' code is not converted by Instant VB:
'		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)
	End Sub
End Class
$vbLabelText   $csharpLabel

Neste exemplo, usamos o método Unsafe.AsPointer para obter um ponteiro do Span. Isso nos permite usar código inseguro ao trabalhar diretamente com ponteiros.

Lembre-se: ao trabalhar com memória não gerenciada, é crucial gerenciar a alocação e a desalocação adequadamente para evitar vazamentos de memória. Sempre libere a memória não gerenciada usando métodos apropriados, como Marshal.FreeHGlobal(). Além disso, tenha cautela ao usar código inseguro, pois ele pode introduzir riscos potenciais de segurança se não for tratado adequadamente.

Span e chamadas de método assíncronas

Utilizar Span em conjunto com chamadas de métodos assíncronas em C# é uma combinação poderosa, especialmente ao lidar com grandes quantidades de dados ou operações de E/S. O objetivo é lidar com operações assíncronas de forma eficiente, sem copiar dados desnecessariamente. Vamos explorar como você pode aproveitar o Span em cenários assíncronos:

1. Operações de E/S assíncronas:

Ao lidar com operações de I/O assíncronas, como ler ou gravar dados em um fluxo, você pode usar Memory ou Span para trabalhar eficientemente com os dados sem criar buffers adicionais.

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
    }
}
Imports System
Imports System.IO
Imports System.Threading.Tasks

Friend Class Program
	Private Shared 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 Shared Sub ProcessData(ByVal data As Span(Of Byte))
		' Perform operations on the data
	End Sub
End Class
$vbLabelText   $csharpLabel

Neste exemplo, o método ReadAsync lê dados de um fluxo para o buffer de forma assíncrona. O método ProcessData processa os dados diretamente do Span. sem copiá-lo para outro buffer.

2. Operações de Arquivo Assíncronas:

Assim como nas operações de E/S, ao lidar com operações assíncronas de arquivos, você pode usar o Span para processar dados de forma eficiente sem cópias adicionais.

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
    }
}
Imports System
Imports System.IO
Imports System.Threading.Tasks

Friend Class Program
	Private Shared 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 Shared Sub ProcessData(ByVal data As Span(Of Byte))
		' Perform operations on the data
	End Sub
End Class
$vbLabelText   $csharpLabel

Aqui, o método ReadAsync lê dados de um fluxo de arquivo para o buffer, e o método ProcessData processa os dados diretamente do Span. .

3. Processamento de tarefas assíncronas:

Ao trabalhar com tarefas assíncronas que produzem ou consomem dados, você pode usar Memory ou Span para evitar cópias desnecessárias.

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}");
    }
}
Imports System
Imports System.Linq
Imports System.Threading.Tasks

Friend Class Program
	Private Shared 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

	Shared 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
End Class
$vbLabelText   $csharpLabel

Neste exemplo, o método ProcessDataAsync processa os dados de forma assíncrona e retorna o comprimento dos dados processados ​​sem exigir cópias adicionais.

Apresentando o IronPDF

A biblioteca IronPDF é a mais recente biblioteca C# para PDF da Iron Software , que pode ser usada para gerar documentos PDF visualmente atraentes de forma dinâmica e instantânea usando código C#. O IronPDF oferece uma variedade de recursos, como geração de PDF a partir de HTML, conversão de conteúdo HTML para PDF, mesclagem ou divisão de arquivos PDF, etc.

A principal funcionalidade do IronPDF é a conversão de HTML para PDF , que preserva layouts e estilos. Ele pode gerar PDFs a partir de conteúdo da web, sendo ótimo para relatórios, faturas e documentação. Esta ferramenta permite converter arquivos HTML, URLs e strings HTML em arquivos 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

Instalação

O IronPDF pode ser instalado usando o gerenciador de pacotes NuGet para o console do IronPDF ou usando o gerenciador de pacotes do Visual Studio.

dotnet add package IronPdf
// Or
Install-Package IronPdf

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

using System;
using IronPdf;

class Program
{
    static void Main()
    {
        Console.WriteLine("Generating PDF using IronPDF.");
        var displayFirstName = "<p>First Name is Joe</p>".AsSpan();
        var displayLastName = "<p>Last Name is Doe</p>".AsSpan();
        var displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan();
        var start = "<html><body>".AsSpan();
        var end = "</body></html>".AsSpan();
        var content = string.Concat(start.ToString(), displayFirstName.ToString(), displayLastName.ToString(), displayAddress.ToString(), end.ToString());
        var pdfDocument = new ChromePdfRenderer();
        pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf");
    }
}
using System;
using IronPdf;

class Program
{
    static void Main()
    {
        Console.WriteLine("Generating PDF using IronPDF.");
        var displayFirstName = "<p>First Name is Joe</p>".AsSpan();
        var displayLastName = "<p>Last Name is Doe</p>".AsSpan();
        var displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan();
        var start = "<html><body>".AsSpan();
        var end = "</body></html>".AsSpan();
        var content = string.Concat(start.ToString(), displayFirstName.ToString(), displayLastName.ToString(), displayAddress.ToString(), end.ToString());
        var pdfDocument = new ChromePdfRenderer();
        pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf");
    }
}
Imports System
Imports IronPdf

Friend Class Program
	Shared Sub Main()
		Console.WriteLine("Generating PDF using IronPDF.")
		Dim displayFirstName = "<p>First Name is Joe</p>".AsSpan()
		Dim displayLastName = "<p>Last Name is Doe</p>".AsSpan()
		Dim displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan()
		Dim start = "<html><body>".AsSpan()
		Dim [end] = "</body></html>".AsSpan()
		Dim content = String.Concat(start.ToString(), displayFirstName.ToString(), displayLastName.ToString(), displayAddress.ToString(), [end].ToString())
		Dim pdfDocument = New ChromePdfRenderer()
		pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf")
	End Sub
End Class
$vbLabelText   $csharpLabel

Neste exemplo, estamos usando o Span juntamente com o IronPDF para gerar um documento PDF.

Saída:

C# Span (Como Funciona Para Desenvolvedores): Figura 2 - Saída do Console

PDF Gerado:

C# Span (Como Funciona Para Desenvolvedores): Figura 3 - Saída do PDF

Licenciamento (Teste Gratuito Disponível)

Informações sobre a licença do IronPDF . Essa chave precisa ser inserida no arquivo appsettings.json.

"IronPdf.LicenseKey": "your license key"

Forneça seu e-mail para obter uma licença de avaliação.

Conclusão

Span em C# fornece uma maneira poderosa e eficiente de trabalhar com memória, oferecendo benefícios em termos de desempenho e flexibilidade. Sua natureza contígua e sem dependência de memória a torna particularmente adequada para cenários onde minimizar alocações e cópias de memória é crucial. Ao utilizar o Span, os desenvolvedores podem obter melhor desempenho em uma variedade de aplicações, desde manipulação de strings até processamento numérico de alto desempenho. Ao entender seus recursos e considerar suas limitações, os desenvolvedores podem aproveitar Span para várias tarefas de manipulação de memória de forma segura e eficiente. Juntamente com a visão geral da biblioteca IronPDF , ela pode ser usada para gerar documentos PDF incríveis sem as limitações de await e yield.

Por favor, visite a página de documentação de início rápido do IronPDF .

Perguntas frequentes

O que é Span em C# e por que é importante?

Span é um tipo introduzido no C# 7.2 que representa uma região contígua de memória. É importante porque permite aos desenvolvedores realizar operações de memória de baixo nível de forma eficiente, sem a sobrecarga de alocações no heap, mantendo a segurança e o desempenho do C#.

Como o Span otimiza a manipulação de memória em C#?

Span otimiza a manipulação de memória ao fornecer uma abstração de zero-cópia sobre a memória, permitindo que os desenvolvedores referenciem blocos de memória existentes sem duplicar dados. Isso leva a melhorias de desempenho, particularmente em aplicações que lidam com grandes volumes de dados.

Qual é a diferença entre Span e ReadOnlySpan?

Span é uma visão mutável da memória, permitindo modificações, enquanto ReadOnlySpan fornece uma visão somente leitura. ReadOnlySpan é usado quando os dados não devem ser modificados, oferecendo benefícios de desempenho semelhantes enquanto garante a integridade dos dados.

Span pode ser usado com memória não gerenciada em C#?

Sim, Span pode ser usado com memória não gerenciada criando um span a partir de um ponteiro para memória não gerenciada. Isso permite a manipulação direta da memória enquanto garante que ela seja devidamente alocada e desalocada usando métodos como Marshal.AllocHGlobal e Marshal.FreeHGlobal.

Como o IronPDF integra-se com Span para geração de PDFs?

O IronPDF pode trabalhar juntamente com Span para gerar PDFs dinamicamente, gerenciando eficientemente a memória e evitando alocações desnecessárias. Esta integração permite aos desenvolvedores criar documentos PDF a partir de conteúdo web com desempenho melhorado.

Quais são as limitações de usar Span para gerenciamento de memória?

As limitações de usar Span incluem a exigência de memória contígua, falta de gerenciamento automático de memória e coleta de lixo, e ausência de suporte para memória não contígua. Os desenvolvedores devem garantir manualmente a validade e os limites da memória.

Como instalar o IronPDF para manipulação de PDFs em C#?

O IronPDF pode ser instalado em um projeto C# usando o gerenciador de pacotes NuGet. Use comandos como dotnet add package IronPDF ou Install-Package IronPDF para adicioná-lo ao seu projeto.

Quais são os benefícios de usar Span para manipulação de strings?

Span permite a manipulação eficiente de strings minimizando alocações e cópias de memória. Isso é particularmente benéfico em códigos críticos de desempenho onde grandes quantidades de dados de string são processadas.

Existe alguma versão de avaliação disponível para o IronPDF?

Sim, o IronPDF oferece uma licença de avaliação que pode ser obtida fornecendo seu e-mail. A chave da licença de avaliação deve ser inserida no arquivo appsettings.json para usar a biblioteca.

Span pode ser usado em chamadas de métodos assíncronos?

Sim, Span pode ser usado em chamadas de métodos assíncronos para lidar com dados de forma eficiente sem cópias desnecessárias. Isso é particularmente útil em operações de I/O e processamento de arquivos, aproveitando Memory ou Span.

Curtis Chau
Redator Técnico

Curtis Chau é bacharel em Ciência da Computação (Universidade Carleton) e se especializa em desenvolvimento front-end, com experiência em Node.js, TypeScript, JavaScript e React. Apaixonado por criar interfaces de usuário intuitivas e esteticamente agradáveis, Curtis gosta de trabalhar com frameworks modernos e criar manuais ...

Leia mais

Equipe de Suporte Iron

Estamos online 24 horas por dia, 5 dias por semana.
Bater papo
E-mail
Liga para mim