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 do Span. struct 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 Span

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

ReadOnlySpan

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 visualização somente leitura de um bloco de memória. Isso significa que você não pode modificar os elementos por meio 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 os tipos de coleta tradicionais, especialmente ao lidar com grandes quantidades de dados, pois reduz a necessidade de cópia.

4. Sem verificação de limites

Assim como com 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 fatiamento, permitindo criar sub-intervalos que referenciam uma porção 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 no 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

Somente leitura 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 Span será utilizado. 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

Desde Span Não possui memória própria e, portanto, não está sujeito à coleta de lixo. Portanto, é preciso ter cuidado ao trabalhar com memória alocada na pilha ou com memória que tenha um tempo de vida menor que o do Span. em si.

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

Span e ReadOnlySpan Não realizar 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 sejam executadas corretamente. permanecer dentro dos limites da memória subjacente.

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

Span Foi projetado para funcionar com memória contígua. Se você tiver memória não contígua ou precisar representar estruturas de dados mais complexas, use Span. Pode não ser a escolha mais adequada.

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

Enquanto Span Suporta muitas operações comuns, como fatiamento, indexação e iteração, mas nem todas as operações são suportadas. Por exemplo, não é possível redimensionar um Span. E certas operações que envolvem a alteração do tamanho da memória subjacente não são permitidas.

1.6 Compatibilidade limitada com a plataforma

Enquanto Span Faz parte do .NET Standard e do .NET Core, portanto pode não estar disponível em todas as plataformas ou ambientes. É crucial garantir que a plataforma de destino seja compatível com o Span. Se você 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

Somente leitura Foi projetado principalmente para funcionar perfeitamente com blocos de memória ou buffers contíguos. 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

Determinadas estruturas de dados ou cenários que dependem de memória não contígua podem não se adequar bem ao ReadOnlySpan. . Por exemplo, estruturas de dados como listas encadeadas ou estruturas de grafos 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 exigem aritmética de ponteiros complexa, ReadOnlySpan Podem não oferecer o mesmo controle e flexibilidade de baixo nível que 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

Assim como ocorre com a memória contígua, é importante observar que nem todas as APIs ou bibliotecas oferecem suporte direto à 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, em seguida, 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

O 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> a partir 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 o trabalho for concluído.

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 E/S assíncronas, como leitura ou gravação de dados em um fluxo, você pode usar a memória. ou Span para trabalhar com os dados de forma eficiente, 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 a memória. 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 (Como funciona para desenvolvedores): Figura 1 - Instale o IronPDF usando o Gerenciador de Pacotes NuGet pesquisando ironpdf na barra de pesquisa do Gerenciador de Pacotes NuGet

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 em 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#, o uso de memória oferece uma maneira poderosa e eficiente de trabalhar com ela, proporcionando 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 compreender suas funcionalidades e considerar suas limitações, os desenvolvedores podem aproveitar ao máximo o Span. Para diversas 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 isso é importante?

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

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

Span Otimiza a manipulação de memória ao fornecer uma abstração de cópia zero sobre a memória, permitindo que os desenvolvedores referenciem blocos de memória existentes sem duplicar dados. Isso leva a melhorias de desempenho, principalmente em aplicativos 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 visualização somente leitura. ReadOnlySpan É utilizado quando os dados não devem ser modificados, oferecendo benefícios de desempenho semelhantes e, ao mesmo tempo, garantindo a integridade dos dados.

Pode abranger Pode ser usado com memória não gerenciada em C#?

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

Como o IronPDF se integra ao Span? para geração de PDF?

O IronPDF pode funcionar em conjunto com o Span. Para gerar PDFs dinamicamente, gerenciando a memória de forma eficiente e evitando alocações desnecessárias. Essa integração permite que os desenvolvedores criem documentos PDF a partir de conteúdo da web com desempenho aprimorado.

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

As limitações do uso do Span Isso inclui a exigência de memória contígua, a falta de gerenciamento automático de memória e coleta de lixo, e a 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 o Span? para manipulação de strings?

Span Permite a manipulação eficiente de strings, minimizando a alocação e a cópia de memória. Isso é particularmente benéfico em códigos críticos para o desempenho, onde grandes quantidades de dados em formato 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.

Pode abranger Pode ser usado em chamadas de métodos assíncronos?

Sim, Span Pode ser usado em chamadas de métodos assíncronos para manipular dados de forma eficiente, sem cópias desnecessárias. Isso é particularmente útil em operações de E/S e processamento de arquivos, aproveitando Memory disponível. 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 de ferro

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