Passer au contenu du pied de page
.NET AIDE

C# Span (Comment cela fonctionne pour les développeurs)

Span est un type introduit dans C# 7.2 comme partie de la structure Span dans l'espace de noms System. Il est conçu pour représenter une région contiguë de mémoire arbitraire. Contrairement aux tableaux ou collections telles que le tas géré, Span ne possède pas la mémoire de la pile ou la région de mémoire à laquelle il pointe ; au lieu de cela, il fournit une vue légère sur des blocs de mémoire existants. Cette caractéristique rend Span particulièrement puissant dans les scénarios où vous devez travailler efficacement avec des tampons mémoire sans entraîner de surcoût supplémentaire et de situations de code non sécurisé. Later in this article, we shall also see the introduction to the IronPDF library from Iron Software.

Caractéristiques clés de Span

1. Gestion de la mémoire

Span en C# permet aux développeurs de travailler directement avec la mémoire sans recourir à des allocations traditionnelles sur le tas. Il offre un moyen de créer des tranches de mémoire à partir de tableaux existants ou d'autres sources de mémoire, éliminant ainsi le besoin de copies de mémoire supplémentaires.

2. Abstractions sans copie

L'une des caractéristiques remarquables de C# Span est ses abstractions sans copie. Au lieu de dupliquer les données, Span fournit un moyen de référencer efficacement la mémoire existante. Ceci est particulièrement bénéfique dans les scénarios où copier de grandes quantités de données serait irréalisable ou trop coûteux.

3. Opérations similaires aux pointeurs

Bien que C# ait traditionnellement été un langage de haut niveau et sécurisé, Span introduit un certain degré de manipulation de mémoire bas niveau, semblable au travail avec des pointeurs dans des langages comme C ou C++. Les développeurs peuvent effectuer des opérations similaires aux pointeurs sans compromettre la sécurité et la nature gérée de C#.

4. Nature immuable

Malgré ses capacités d'accès à la mémoire bas niveau, C# Span reste immuable. Cela signifie que, bien qu'il permette la manipulation de la mémoire, il impose la sécurité en empêchant les modifications non désirées.

Exemple

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

Alors que Span est mutable et permet des modifications des données sous-jacentes, ReadOnlySpan est une vue immuable de la mémoire. Il fournit une interface en lecture seule à une région contiguë de mémoire, le rendant adapté aux scénarios où vous avez juste besoin de lire les données sans les modifier.

Voici quelques points clés.

1. Vue en lecture seule

Comme son nom l'indique, ReadOnlySpan vous permet de créer une vue en lecture seule d'un bloc de mémoire. Cela signifie que vous ne pouvez pas modifier les éléments à travers ReadOnlySpan.

2. Représentation de la mémoire

Comme Span, ReadOnlySpan ne possède pas la mémoire à laquelle il pointe. Il se réfère à la mémoire existante et peut pointer vers des tableaux, de la mémoire allouée sur la pile ou de la mémoire native.

3. Avantages de performance

Comme Span, ReadOnlySpan peut offrir de meilleures performances par rapport aux types de collection traditionnels, notamment lorsqu'il s'agit de grandes quantités de données, car il réduit le besoin de copies.

4. Pas de vérification des limites

Comme avec Span, ReadOnlySpan ne vérifie pas les limites. C'est la responsabilité du développeur d'assurer que les opérations restent dans les limites de la mémoire sous-jacente.

5. Utilisation avec les tranches de tableau

ReadOnlySpan supporte le découpage, vous permettant de créer des sous-tranches qui référencent une partie de la mémoire d'origine.

Exemple

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

Il existe de nombreuses façons différentes de créer ReadOnlySpan et de travailler avec. Voici quelques exemples.

1. Créer ReadOnlySpan à partir d'une chaîne

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. Travailler avec des sous-chaînes

Utilisez Slice sur 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. Passer une sous-chaîne à une méthode

Passez ReadOnlySpan comme paramètre à la méthode.

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. Recherche dans une chaîne

ReadOnlySpan pour la recherche dans une chaîne avec 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. Utilisation des fichiers mappés en mémoire

ReadOnlySpan peut être plus efficace avec les fichiers mappés en mémoire.

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. Manipulation de chaînes efficace

ReadOnlySpan peut être utilisé pour une manipulation de chaînes efficace.

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. Passer une sous-chaîne aux API

Lors de l'utilisation de bibliothèques externes ou d'API qui fonctionnent sur des étendues de caractères.

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

ReadOnlySpan offre un moyen de travailler avec des chaînes plus efficacement, notamment dans les scénarios où les allocations de mémoire et les copies doivent être minimisées. C'est un outil puissant pour optimiser le code critique en termes de performance et peut être particulièrement bénéfique lorsqu'il s'agit de grandes quantités de données de chaîne.

Limites de Span

Bien que Span en C# soit une fonctionnalité puissante avec de nombreux avantages, il vient avec certaines limitations et considérations, notamment dans le contexte de la mémoire contiguë et non contiguë. Explorons ces limitations :

1. Tampons de mémoire contiguë

1.1 Pas de gestion automatique de la mémoire

Span ne gère pas la mémoire à laquelle il pointe. Cela signifie que si la mémoire gérée sous-jacente est libérée ou sort du scope, utiliser Span entraînera un comportement indéfini ou des plantages potentiels. Les développeurs doivent s'assurer que la mémoire sous-jacente est toujours valide lorsqu'ils utilisent un Span.

1.2 Pas de collecte des déchets

Puisque Span ne possède pas de mémoire, il n'est pas sujet à la collecte des déchets. Vous devez donc être prudent lorsque vous travaillez avec de la mémoire allouée sur la pile ou de la mémoire qui a une durée de vie plus courte que Span lui-même.

1.3 Vérification des limites désactivée

Span et ReadOnlySpan ne vérifient pas les limites par défaut. Cela peut mener à accéder à des emplacements de mémoire invalides s'ils ne sont pas utilisés avec précaution. Les développeurs doivent manuellement s'assurer que les opérations sur un Span restent dans les limites de la mémoire sous-jacente.

1.4 Pas de support pour la mémoire non contiguë

Span est conçu pour fonctionner avec de la mémoire contiguë. Si vous avez de la mémoire non contiguë ou que vous devez représenter des structures de données plus complexes, Span peut ne pas être le choix le plus approprié.

1.5 Toutes les opérations ne sont pas supportées

Bien que Span supporte de nombreuses opérations courantes comme le découpage, l'indexation et l'itération, toutes les opérations ne sont pas supportées. Par exemple, vous ne pouvez pas redimensionner un Span, et certaines opérations impliquant le changement de la longueur de la mémoire sous-jacente ne sont pas autorisées.

1.6 Compatibilité limitée des plateformes

Bien que Span fasse partie de .NET Standard et .NET Core, il peut ne pas être disponible sur toutes les plateformes ou environnements. Il est crucial de s'assurer que votre plateforme cible supporte Span si vous prévoyez de l'utiliser dans votre code.

2. Tampons de mémoire non contiguës

2.1 Support limité pour la mémoire non contiguë

ReadOnlySpan est principalement conçu pour fonctionner de manière transparente avec des blocs ou tampons de mémoire contigus. Il peut ne pas être le choix le plus approprié pour les scénarios où des tampons de mémoire non contigus ou des structures avec des lacunes dans la mémoire sont impliqués.

2.2 Limitations structurelles

Certaines structures de données ou scénarios qui reposent sur une mémoire non contiguë peuvent ne pas bien s'aligner avec ReadOnlySpan. Par exemple, des structures de données comme les listes chaînées ou les structures de graphes pourraient ne pas être bien adaptées en raison de l'exigence de mémoire contiguë de ReadOnlySpan.

2.3 Opérations complexes sur les pointeurs

Dans les situations impliquant de la mémoire non contiguë, en particulier celles nécessitant des calculs pointus complexes, ReadOnlySpan pourrait ne pas offrir le même contrôle bas niveau et la même flexibilité que les pointeurs bruts dans des langages comme C++. Dans de tels cas, utiliser du code non sécurisé avec des pointeurs pourrait être plus approprié.

2.4 Absence de support direct dans certaines API

De façon similaire à la mémoire contiguë, il est important de noter que toutes les API ou bibliothèques ne peuvent pas directement supporter la mémoire non contiguë représentée par ReadOnlySpan. S'adapter à de tels scénarios peut nécessiter des étapes ou des conversions intermédiaires pour assurer la compatibilité.

Span et mémoire non gérée

En C#, Span peut être utilisé efficacement avec de la mémoire non gérée pour effectuer des opérations liées à la mémoire de manière contrôlée et efficace. La mémoire non gérée fait référence à la mémoire qui n'est pas gérée par le collecteur de déchets du runtime .NET, et elle implique souvent l'utilisation d'allocations de mémoire native et de désallocations. Voici comment Span peut être utilisé avec de la mémoire non gérée en C#.

Allouer de la mémoire non gérée

Pour allouer de la mémoire non gérée, vous pouvez utiliser la classe System.Runtime.InteropServices.MemoryMarshal. La méthode Marshal.AllocHGlobal alloue de la mémoire et renvoie un pointeur vers le bloc alloué. La mémoire allouée ou l'adresse mémoire est conservée dans un pointeur unmanagedMemory et aura un accès en lecture-écriture. Les régions contiguës de mémoire peuvent être facilement accessibles.

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

Dans le code source ci-dessus, nous allouons un bloc de mémoire non gérée en utilisant Marshal.AllocHGlobal et créons ensuite un Span<byte> à l'aide du pointeur obtenu de la mémoire non gérée. Cela nous permet de travailler avec de la mémoire non gérée en utilisant l'API familière de Span. Il est important de noter que lorsque vous travaillez avec de la mémoire non gérée, vous êtes responsable de gérer l'allocation et la désallocation de la mémoire.

Copier des données vers et depuis la mémoire non gérée

Span fournit des méthodes comme Slice, CopyTo et ToArray qui peuvent être utilisées pour copier efficacement des données entre la mémoire gérée et la mémoire non gérée.

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

Dans cet exemple :

  • Marshal.AllocHGlobal alloue de la mémoire non gérée pour les données de destination.
  • new Span<int>(destinationPointer.ToPointer(), sourceArray.Length) crée un Span<int> à partir de la mémoire non gérée allouée.
  • La méthode sourceSpan.CopyTo(destinationSpan) copie les données du tableau géré à la mémoire non gérée.
  • Les valeurs dans la mémoire de destination sont imprimées pour vérifier l'opération de copie.
  • La méthode Marshal.FreeHGlobal(destinationPointer) est utilisée pour désallouer la mémoire non gérée une fois terminé.

Utiliser du code non sécurisé

Lorsque vous traitez de la mémoire non gérée, vous pouvez également utiliser du code non sécurisé avec des pointeurs. Dans de tels cas, vous pouvez obtenir un pointeur à partir de Span en utilisant la méthode 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

Dans cet exemple, nous utilisons la méthode Unsafe.AsPointer pour obtenir un pointeur à partir de Span. Cela nous permet d'utiliser du code non sécurisé lorsque nous travaillons directement avec des pointeurs.

Rappelez-vous, quand vous travaillez avec de la mémoire non gérée, il est crucial de gérer correctement l'allocation et la désallocation pour éviter les fuites de mémoire. Libérez toujours la mémoire non gérée en utilisant des méthodes appropriées, telles que Marshal.FreeHGlobal(). De plus, faites preuve de prudence lorsque vous utilisez du code non sécurisé, car cela peut introduire des risques de sécurité potentiels si ce n'est pas manipulé correctement.

Span et appels de méthode asynchrones

Utiliser Span en conjonction avec des appels de méthode asynchrones en C# est une combinaison puissante, notamment lors du traitement de grandes quantités de données ou d'opérations d'E/S. Le but est de gérer efficacement les opérations asynchrones sans copie de données inutiles. Voyons comment tirer parti de Span dans des scénarios asynchrones :

1. Opérations d'E/S asynchrones :

Lors de la manipulation d'opérations d'E/S asynchrones, telles que la lecture ou l'écriture de données sur un flux, vous pouvez utiliser Memory ou Span pour travailler efficacement avec les données sans créer de tampons supplémentaires.

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

Dans cet exemple, la méthode ReadAsync lit des données d'un flux dans le tampon de manière asynchrone. La méthode ProcessData traite ensuite les données directement à partir de Span sans les copier dans un autre tampon.

2. Opérations de fichiers asynchrones :

De la même manière que les opérations d'E/S, lors de la manipulation d'opérations de fichiers asynchrones, vous pouvez utiliser Span pour traiter efficacement les données sans copie supplémentaire.

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

Ici, la méthode ReadAsync lit des données d'un flux de fichier dans le tampon, et la méthode ProcessData traite les données directement à partir de Span.

3. Traitement de tâches asynchrones :

Lorsque vous travaillez avec des tâches asynchrones qui produisent ou consomment des données, vous pouvez utiliser Memory ou Span pour éviter des copies inutiles.

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

Dans cet exemple, la méthode ProcessDataAsync traite les données de manière asynchrone et renvoie la longueur des données traitées sans nécessiter de copies supplémentaires.

Présentation d'IronPDF

IronPDF library overview is the latest C# PDF library from Iron Software qui peut être utilisée pour générer dynamiquement de magnifiques documents PDF à la volée en utilisant du code C#. IronPDF fournit une variété de fonctionnalités telles que la génération de PDF à partir de HTML, la conversion de contenu HTML en PDF, la fusion ou la division de fichiers PDF, etc.

La fonctionnalité principale d'IronPDF est sa fonctionnalité HTML en PDF, qui préserve les mises en page et les styles. Elle peut générer des PDF à partir de contenu web, ce qui la rend idéale pour les rapports, les factures et la documentation. Cet outil prend en charge la conversion de fichiers HTML, d'URL et de chaînes HTML en fichiers 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

Installation

IronPDF peut être installé en utilisant le gestionnaire de packages NuGet pour IronPDF ou en utilisant le gestionnaire de packages de Visual Studio.

dotnet add package IronPdf
// Or
Install-Package IronPdf

Span C# (Comment cela fonctionne pour les développeurs) : Figure 1 - Installer IronPDF en utilisant le Gestionnaire de Packages NuGet en cherchant ironpdf dans la barre de recherche du Gestionnaire de Packages 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

Dans cet exemple, nous utilisons Span avec IronPDF pour générer un document PDF.

Sortie :

Span C# (Comment cela fonctionne pour les développeurs) : Figure 2 - Sortie de la console

PDF généré :

Span C# (Comment cela fonctionne pour les développeurs) : Figure 3 - Sortie PDF

Licence (essai gratuit disponible)

Informations sur la licence IronPDF. Cette clé doit être placée dans appsettings.json.

"IronPdf.LicenseKey": "your license key"

Fournissez votre email pour obtenir une licence d'essai.

Conclusion

Span en C# fournit une manière puissante et efficace de travailler avec la mémoire, offrant des avantages en termes de performance et de flexibilité. Sa nature non possessive et contiguë le rend particulièrement adapté aux scénarios où minimiser les allocations de mémoire et les copies est essentiel. En tirant parti de Span, les développeurs peuvent obtenir de meilleures performances dans une variété d'applications, allant de la manipulation de chaînes à la gestion numérique haute performance. En comprenant ses caractéristiques et en considérant ses limitations, les développeurs peuvent utiliser Span pour diverses tâches de manipulation de mémoire de manière sûre et efficace. Avec l'overview de la bibliothèque IronPDF, il peut être utilisé pour générer de superbes documents PDF sans attente et sans barrières d'exécution.

Veuillez visiter la page page de documentation rapide de démarrage d'IronPDF.

Questions Fréquemment Posées

Qu'est-ce que Span en C# et pourquoi est-ce important ?

Span est un type introduit dans C# 7.2 qui représente une région contiguë de mémoire. Il est important car il permet aux développeurs d'effectuer des opérations de mémoire de bas niveau efficacement sans le surcoût des allocations sur le tas, tout en maintenant la sécurité et la performance de C#.

Comment Span optimise-t-il la manipulation de la mémoire en C# ?

Span optimise la manipulation de la mémoire en fournissant une abstraction sans copie sur la mémoire, permettant aux développeurs de référencer des blocs de mémoire existants sans dupliquer les données. Cela conduit à des améliorations de performance, notamment dans les applications qui traitent de grands volumes de données.

Quelle est la différence entre Span et ReadOnlySpan ?

Span est une vue mutable de la mémoire, permettant des modifications, tandis que ReadOnlySpan offre une vue en lecture seule. ReadOnlySpan est utilisé lorsque les données ne doivent pas être modifiées, offrant des avantages similaires en termes de performance tout en assurant l'intégrité des données.

Span peut-il être utilisé avec la mémoire non gérée en C# ?

Oui, Span peut être utilisé avec la mémoire non gérée en créant un span à partir d'un pointeur vers la mémoire non gérée. Cela permet une manipulation directe de la mémoire tout en s'assurant qu'elle est correctement allouée et désallouée en utilisant des méthodes comme Marshal.AllocHGlobal et Marshal.FreeHGlobal.

Comment IronPDF s'intègre-t-il avec Span pour la génération de PDF ?

IronPDF peut fonctionner aux côtés de Span pour générer des PDFs de manière dynamique en gérant efficacement la mémoire et en évitant les allocations inutiles. Cette intégration permet aux développeurs de créer des documents PDF à partir de contenu web avec des performances améliorées.

Quelles sont les limitations de l'utilisation de Span pour la gestion de la mémoire ?

Les limitations de l'utilisation de Span incluent la nécessité d'une mémoire contiguë, l'absence de gestion automatique de la mémoire et de collecte des ordures, et pas de support pour la mémoire non contiguë. Les développeurs doivent manuellement s'assurer de la validité et des limites de la mémoire.

Comment peut-on installer IronPDF pour la manipulation de PDF en C# ?

IronPDF peut être installé dans un projet C# en utilisant le gestionnaire de paquets NuGet. Utilisez des commandes telles que dotnet add package IronPdf ou Install-Package IronPdf pour l'ajouter à votre projet.

Quels sont les avantages de l'utilisation de Span pour la manipulation de chaînes ?

Span permet une manipulation efficace des chaînes en minimisant les allocations de mémoire et les copies. Cela est particulièrement bénéfique dans le code critique en performance où de grandes quantités de données de chaînes sont traitées.

Y a-t-il une version d'essai disponible pour IronPDF?

Oui, IronPDF propose une licence d'essai qui peut être obtenue en fournissant votre adresse email. La clé de licence d'essai doit être placée dans le fichier appsettings.json pour utiliser la bibliothèque.

Span peut-il être utilisé dans des appels de méthodes asynchrones ?

Oui, Span peut être utilisé dans des appels de méthodes asynchrones pour traiter les données efficacement sans copie inutile. Cela est particulièrement utile dans les opérations d'E/S et le traitement de fichiers, en tirant parti de Memory ou Span.

Curtis Chau
Rédacteur technique

Curtis Chau détient un baccalauréat en informatique (Université de Carleton) et se spécialise dans le développement front-end avec expertise en Node.js, TypeScript, JavaScript et React. Passionné par la création d'interfaces utilisateur intuitives et esthétiquement plaisantes, Curtis aime travailler avec des frameworks modernes ...

Lire la suite