Przejdź do treści stopki
POMOC .NET

C# Span (jak to działa dla programistów)

Span to typ wprowadzony w C# 7.2 jako czesc struktury Span w przestrzeni nazw System. Jest zaprojektowany do reprezentowania ciaglego obszaru dowolnej pamieci. W przeciwienstwie do tablic lub kolekcji takich jak zarzadzany stos, Span nie jest właścicielem pamięci stosu ani regionu pamięci, do którego wskazuje; zamiast tego oferuje lekki widok nad istniejacymi blokami pamieci. Ta cecha czyni Span szczególnie potężnym w scenariuszach, gdzie należy pracować z buforami pamięci efektywnie, bez generowania dodatkowych kosztów i ryzyk związanych z niebezpiecznym kodem. Pozniej w artykule zapoznamy sie równiez z biblioteka IronPDF od Iron Software.

Kluczowe cechy Span

1. Zarzadzanie pamiecia

Span w C# pozwala programistom na bezposrednia prace z pamiecia, bez korzystania z tradycyjnych alokacji na stosie. Oferuje sposób na tworzenie wycinków pamięci z istniejących tablic lub innych źródeł pamięci, eliminując potrzebę dodatkowego kopiowania pamięci.

2. Abstrakcje zero-kopiowania

Jedna z wyrozniajacych sie funkcji C# Span jest jego abstrahowanie bez kopiowania. Zamiast duplikowac dane, Span udostępnia sposób efektywnego odniesienia się do istniejącej pamięci. Jest to szczególnie korzystne w scenariuszach, gdy kopiowanie dużych ilości danych byłoby niepraktyczne lub zbyt kosztowne.

3. Operacje podobne do wskaźników

Mimo że C# tradycyjnie jest jezykiem wysokopoziomowym i bezpiecznym, Span wprowadza pewien stopień niskopoziomowej manipulacji pamięcią, podobny do pracy ze wskaźnikami w językach takich jak C czy C++. Programiści mogą wykonywać operacje podobne do wskaźników, nie poświęcając bezpieczeństwa i zarządzanego charakteru C#.

4. Niezmiennosc

Pomimo swoich możliwości dostępu do pamięci niskopoziomowej, C# Span pozostaje niezmienny. Oznacza to, że chociaż pozwala na manipulację pamięcią, wymusza bezpieczeństwo poprzez zapobieganie niezamierzonym modyfikacjom.

Przykład

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

Podczas gdy Span jest zmienny i pozwala na modyfikacje danych leżących u jego podstaw, ReadOnlySpan to niezmienny widok pamięci. Udostępnia interfejs tylko do odczytu dla ciągłego obszaru pamięci, co czyni go odpowiednim w scenariuszach, gdzie potrzebujesz jedynie odczytać dane, nie modyfikując ich.

Oto kilka kluczowych punktów.

1. Widok tylko do odczytu

Jak sama nazwa wskazuje, ReadOnlySpan pozwala utworzyć widok tylko do odczytu na blok pamięci. Oznacza to, że nie możesz zmieniać elementów za pośrednictwem ReadOnlySpan.

2. Reprezentacja pamięci

Podobnie jak Span, ReadOnlySpan nie jest właścicielem pamięci, do której wskazuje. Odnosi się do istniejącej pamięci i może wskazywać na tablice, pamięć stosową lub pamięć natywną.

3. Korzyści wydajnościowe

Podobnie jak Span, ReadOnlySpan może oferować lepszą wydajność w porównaniu z tradycyjnymi typami kolekcji, szczególnie przy pracy z dużymi ilościami danych, ponieważ redukuje potrzebę kopiowania.

4. Brak sprawdzania granic

Podobnie jak Span, ReadOnlySpan nie wykonuje sprawdzania granic. To na programiście leży odpowiedzialność za zapewnienie, że operacje pozostają w granicach pamięci, na której działają.

5. Użycie z wycinkami tablic

ReadOnlySpan wspiera wycinanie, co pozwala tworzyć pod-spany odnoszące się do części oryginalnej pamięci.

Przykład

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

Istnieje wiele różnych sposobów na tworzenie ReadOnlySpan i pracę z nim. Poniżej znajdują się przykłady.

1. Tworzenie ReadOnlySpan ze 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. Praca z podciagami

Użyj Slice na 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. Przekazywanie podciagu do metody

Przekaż ReadOnlySpan jako parametr do metody.

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. Wyszukiwanie w String

ReadOnlySpan do wyszukiwania w napisie z 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. Użycie z plikami mapowanymi w pamieci

ReadOnlySpan może być bardziej efektywny z plikami mapowanymi w pamięci.

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. Efektywna manipulacja napisami

ReadOnlySpan może być używany do efektywnej manipulacji napisami.

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. Przekazywanie podciagu do interfejsów API

Podczas pracy z zewnętrznymi bibliotekami lub API, które operują na rozpiętościach znaków.

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 zapewnia sposób na bardziej efektywną pracę z napisami, szczególnie w scenariuszach, gdy alokacje pamięci i kopiowanie powinny być zminimalizowane. Jest to potężne narzędzie do optymalizacji kodu krytycznego dla wydajności i może być szczególnie korzystne przy pracy z dużymi ilościami danych napisowych.

Ograniczenia Span

Chociaż Span w C# to potężna funkcja z licznymi zaletami, wiąże się z pewnymi ograniczeniami i uwagami, szczególnie w kontekście pamięci ciągłej i nieciągłej. Przyjrzyjmy się tym ograniczeniom:

1. Bufory pamięci ciągłej

1.1 Brak automatycznego zarządzania pamięcią

Span nie zarządza pamięcią, do której wskazuje. Oznacza to, że jeśli zarządzana pamięć ulega zwolnieniu lub wychodzi poza zakres, użycie Span może prowadzić do nieokreślonego zachowania lub potencjalnych awarii. Programista musi zapewnić, że pamięć, na której operuje Span, jest wciąż ważna.

1.2 Brak zbierania śmieci

Ponieważ Span nie jest właścicielem pamięci, nie podlega zbieraniu śmieci. Dlatego musisz być ostrożny, pracując z pamięcią przydzielaną na stosie lub pamięcią, która ma krótszy okres życia niż sam Span.

1.3 Wyłączenie sprawdzania granic

Span i ReadOnlySpan nie wykonują domyślnie sprawdzania granic. Może to prowadzić do dostępu do niepoprawnych lokalizacji pamięci, jeśli używane są nieostrożnie. Programista musi ręcznie zapewnić, że operacje na Span pozostają w granicach pamięci, na której działają.

1.4 Brak wsparcia dla pamięci nieciągłej

Span jest zaprojektowany do pracy z pamięcią ciągłą. Jeśli masz pamięć nieciągłą lub musisz reprezentować bardziej złożone struktury danych, Span może nie być najbardziej odpowiednim wyborem.

1.5 Nie wszystkie operacje są wspierane

Choć Span wspiera wiele powszechnych operacji takich jak wycinanie, indeksowanie i iterowanie, nie wszystkie operacje są wspierane. Na przykład, nie możesz zmieniać rozmiaru Span ani wykonywać operacji, które wymagają zmiany długości pamięci leżącej u jego podstawy.

1.6 Ograniczona kompatybilność platformowa

Choć Span jest częścią .NET Standard i .NET Core, może nie być dostępny na wszystkich platformach lub w środowiskach. Konieczne jest zapewnienie, że docelowa platforma obsługuje Span, jeśli planujesz go używać w swoim kodzie.

2. Bufory pamięci nieciągłej

2.1 Ograniczona obsługa pamięci nieciągłej

ReadOnlySpan jest przede wszystkim projektowany do bezproblemowej współpracy z ciągłymi blokami pamięci lub buforami. Może nie być najbardziej odpowiednim wyborem w scenariuszach, gdzie występują bufory pamięci nieciągłej lub struktury z lukami w pamięci.

2.2 Ograniczenia strukturalne

Niektóre struktury danych lub scenariusze opierające się na pamięci nieciągłej mogą nie być zgodne z ReadOnlySpan. Na przykład struktury danych takie jak listy związane lub struktury grafowe mogą nie być dobrze dostosowane ze względu na wymaganie pamięci ciągłej przez ReadOnlySpan.

2.3 Złożone operacje na wskaźnikach

W sytuacjach obejmujących pamięć nieciągłą, szczególnie wymagających zawiłej arytmetyki wskaźnikowej, ReadOnlySpan może nie oferować tego samego niskopoziomowego sterowania i elastyczności, co surowe wskaźniki w językach takich jak C++. W takich przypadkach korzystanie z niebezpiecznego kodu ze wskaźnikami może być bardziej odpowiednie.

2.4 Brak bezpośredniego wsparcia w niektórych API

Podobnie jak w przypadku pamięci ciągłej, ważne jest zauważenie, że nie wszystkie API lub biblioteki mogą bezpośrednio obsługiwać pamięć nieciągłą reprezentowaną przez ReadOnlySpan. Dostosowanie się do takich scenariuszy może wymagać dodatkowych kroków pośrednich lub konwersji w celu zapewnienia kompatybilności.

Span i pamiec niezarzadzana

W C# Span moze byc skutecznie wykorzystywany z pamiecia niezarzadzana do przeprowadzania operacji zwiazanych z pamiecia w kontrolowany i efektywny sposób. Pamiec niezarzadzana odnosi sie do pamieci, ktora nie jest zarzadzana przez zbieracza smieci w czasie trwania .NET, i czesto wiąże się z użyciem natywnych alokacji i dealokacji pamięci. Oto jak Span może być stosowany z pamięcią niezarządzaną w C#.

Alokowanie pamieci niezarzadzanej

Aby przeprowadzić alokację pamięci niezarządzanej, można użyć klasy System.Runtime.InteropServices.MemoryMarshal. Metoda Marshal.AllocHGlobal alokuje pamięć i zwraca wskaźnik na przydzielony blok. Przydzielona pamiec lub adres pamieci jest przechowywany we wskazniku unmanagedMemory i bedzie miec dostep odczyt-zapis. Ciągłe obszary pamięci można łatwo uzyskać.

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

W powyższym kodzie źródłowym przydzielamy blok pamięci niezarządzanej przy użyciu Marshal.AllocHGlobal, a następnie tworzymy Span<byte> za pomocą wskaźnika uzyskanego z pamięci niezarządzanej. Pozwala nam to pracować z pamięcią niezarządzaną, korzystając z dobrze znanego API Span. Ważne jest zauważenie, że podczas pracy z pamięcią niezarządzaną, odpowiadasz za zarządzanie alokacją i dealokacją pamięci.

Kopiowanie danych do i z pamieci niezarzadzanej

Span zapewnia metody takie jak Slice, CopyTo i ToArray, które można używać do efektywnego kopiowania danych między pamięcią zarządzaną a niezarządzaną.

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

W tym przykładzie:

  • Marshal.AllocHGlobal alokuje pamięć niezarządzaną dla danych docelowych.
  • new Span<int>(destinationPointer.ToPointer(), sourceArray.Length) tworzy Span<int> z przydzielonej pamięci niezarządzanej.
  • Metoda sourceSpan.CopyTo(destinationSpan) kopiuje dane z zarządzanej tablicy do pamięci niezarządzanej.
  • Wartości w pamięci docelowej są drukowane w celu weryfikacji operacji kopiowania.
  • Metoda Marshal.FreeHGlobal(destinationPointer) jest używana do dealokacji pamięci niezarządzanej po zakończeniu.

Korzystanie z kodu niebezpiecznego

Podczas pracy z pamięcią niezarządzaną możesz również użyć kodu niebezpiecznego z wskaźnikami. W takich przypadkach możesz uzyskać wskaźnik z Span, używając metody 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

W tym przykładzie używamy metody Unsafe.AsPointer, aby uzyskać wskaźnik z Span. Pozwala to na użycie kodu niebezpiecznego przy bezpośredniej pracy ze wskaźnikami.

Pamiętaj, że podczas pracy z pamięcią niezarządzaną kluczowe jest prawidłowe zarządzanie alokacjami i dealokacjami, aby uniknąć wycieków pamięci. Zawsze zwalniaj pamięć niezarządzaną za pomocą odpowiednich metod, takich jak Marshal.FreeHGlobal(). Dodatkowo, bądź ostrożny przy używaniu kodu niebezpiecznego, ponieważ może on wprowadzić potencjalne ryzyka bezpieczeństwa, jeśli nie jest prawidłowo obsługiwany.

Span i asynchroniczne wywołania metod

Korzystanie ze Span w połączeniu z asynchronicznymi wywołaniami metod w C# to potężne połączenie, zwłaszcza przy operacjach na dużych ilościach danych lub operacjach I/O. Celem jest efektywne zarządzanie operacjami asynchronicznymi bez niepotrzebnego kopiowania danych. Przyjrzyjmy się, jak można wykorzystać Span w scenariuszach asynchronicznych:

1. Asynchroniczne operacje I/O:

Podczas pracy z asynchronicznymi operacjami I/O, takimi jak odczytywanie lub zapisywanie danych do strumienia, można użyć Memory lub Span, aby efektywnie pracować z danymi bez tworzenia dodatkowych buforów.

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

W tym przykladzie metoda ReadAsync asynchronicznie odczytuje dane z strumienia do buforu. Metoda ProcessData następnie przetwarza dane bezpośrednio z Span, bez kopiowania ich do innego buforu.

2. Asynchroniczne operacje na plikach:

Podobnie jak w operacjach I/O, podczas pracy z asynchronicznymi operacjami na plikach można użyć Span, aby efektywnie przetwarzać dane bez dodatkowego kopiowania.

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

Tutaj metoda ReadAsync odczytuje dane z strumienia plików do bufora, a metoda ProcessData przetwarza dane bezposrednio z Span.

3. Asynchroniczne przetwarzanie zadań:

Podczas pracy z asynchronicznymi zadaniami, które produkują lub konsumują dane, można użyć Memory lub Span, aby uniknąć niepotrzebnego kopiowania.

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

W tym przykladzie metoda ProcessDataAsync przetwarza dane asynchronicznie i zwraca długość przetworzonych danych bez potrzeby dodatkowego kopiowania.

Przedstawiamy IronPDF

Przegląd biblioteki IronPDF to najnowsza biblioteka PDF w C# od Iron Software, która może być używana do dynamicznego generowania pięknych dokumentów PDF za pomocą kodu C#. IronPDF zapewnia róznego rodzaju funkcje, takie jak generowanie PDF z HTML, konwertowanie zawartości HTML na PDF, scalanie lub dzielenie plików PDF itd.

Główną cechą IronPDF jest jego funkcjonalność HTML do PDF, która zachowuje układy i style. Może generować dokumenty PDF z treści internetowych, co czyni go idealnym do raportów, faktur i dokumentacji. To narzędzie obsługuje konwertowanie plików HTML, adresów URL i ciągów HTML na pliki 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

Instalacja

IronPDF można zainstalować za pomocą menedżera pakietów NuGet dla IronPDF z konsoli lub używając menedżera pakietów 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

W tym przykładzie używamy Span w połączeniu z IronPDF, aby generować dokumenty PDF.

Wynik:

C# Span (jak działa dla programistów): Rysunek 2 - Dane wyjściowe konsoli

Wygenerowany PDF:

C# Span (jak działa dla programistów): Rysunek 3 - Dane wyjściowe PDF

Licencjonowanie (dostępna bezpłatna wersja próbna)

Informacje o licencji IronPDF. Ten klucz należy umieścić w pliku appsettings.json.

"IronPdf.LicenseKey": "your license key"

Podaj swój adres e-mail, aby otrzymać Licencję Trial.

Wnioski

Span w C# zapewnia potężny i efektywny sposób pracy z pamięcią, oferując korzyści w zakresie wydajności i elastyczności. Jego nieposiadająca własności, ciągła natura czyni go szczególnie odpowiednim w scenariuszach, gdzie minimalizowanie alokacji pamięci i kopiowania jest kluczowe. Wykorzystując Span, programiści mogą osiągnąć lepszą wydajność w różnorodnych aplikacjach, od manipulacji napisami po przetwarzanie numeryczne o wysokiej wydajności. Rozumiejąc jego cechy i uwzględniając jego ograniczenia, programiści mogą wykorzystywać Span do różnych zadań manipulacji pamięcią bezpiecznie i efektywnie. Wraz z przeglądem biblioteki IronPDF, można go używać do generowania niesamowitych dokumentów PDF bez czekania na zakończenie i granic powrotu.

Odwiedź stronę szybki start dokumentacji IronPDF.

Często Zadawane Pytania

Czym jest Span w języku C# i dlaczego jest ważny?

Span to typ wprowadzony w C# 7.2, który reprezentuje ciągły region pamięci. Jest on ważny, ponieważ pozwala programistom na wydajne wykonywanie niskopoziomowych operacji na pamięci bez obciążania alokacji sterty, zachowując bezpieczeństwo i wydajność języka C#.

W jaki sposób Span optymalizuje manipulowanie pamięcią w języku C#?

Span optymalizuje manipulację pamięcią, zapewniając abstrakcję zerowej kopii nad pamięcią, umożliwiając programistom odwoływanie się do istniejących bloków pamięci bez duplikowania danych. Prowadzi to do poprawy wydajności, szczególnie w aplikacjach obsługujących duże ilości danych.

Jaka jest różnica między Span i ReadOnlySpan?

Span to zmienny widok pamięci, umożliwiający modyfikacje, podczas gdy ReadOnlySpan zapewnia widok tylko do odczytu. ReadOnlySpan jest używany, gdy dane nie powinny być modyfikowane, oferując podobne korzyści w zakresie wydajności przy jednoczesnym zapewnieniu integralności danych.

Czy Span może być używany z niezarządzaną pamięcią w C#?

Tak, Span może być używany z niezarządzaną pamięcią poprzez utworzenie span ze wskaźnika do niezarządzanej pamięci. Pozwala to na bezpośrednią manipulację pamięcią przy jednoczesnym zapewnieniu jej prawidłowej alokacji i dealokacji przy użyciu metod takich jak Marshal.AllocHGlobal i Marshal.FreeHGlobal.

W jaki sposób IronPDF integruje się ze Span w celu generowania plików PDF?

IronPDF może współpracować ze Span w celu dynamicznego generowania plików PDF poprzez efektywne zarządzanie pamięcią i unikanie niepotrzebnych alokacji. Ta integracja umożliwia programistom tworzenie dokumentów PDF z treści internetowych z lepszą wydajnością.

Jakie są ograniczenia używania Span do zarządzania pamięcią?

Ograniczenia korzystania ze Span obejmują wymóg ciągłej pamięci, brak automatycznego zarządzania pamięcią i odśmiecania oraz brak wsparcia dla pamięci nieciągłej. Programiści muszą ręcznie zapewnić ważność i granice pamięci.

Jak można zainstalować IronPDF do manipulacji PDF w C#?

IronPDF można zainstalować w projekcie C# za pomocą menedżera pakietów NuGet. Użyj poleceń takich jak dotnet add package IronPdf lub Install-Package IronPdf, aby dodać go do swojego projektu.

Jakie są korzyści z używania Span do manipulowania ciągami znaków?

Span umożliwia wydajne manipulowanie ciągami znaków poprzez minimalizację alokacji pamięci i kopiowania. Jest to szczególnie korzystne w kodzie o krytycznym znaczeniu dla wydajności, w którym przetwarzane są duże ilości danych łańcuchowych.

Czy dostępna jest wersja próbna IronPDF?

Tak, IronPDF oferuje licencję testową, którą można uzyskać, podając swój email. Klucz licencji testowej należy umieścić w pliku appsettings.json, aby użyć biblioteki.

Czy Span może być używany w asynchronicznych wywołaniach metod?

Tak, Span może być używany w asynchronicznych wywołaniach metod do wydajnej obsługi danych bez zbędnego kopiowania. Jest to szczególnie przydatne w operacjach I/O i przetwarzaniu plików, wykorzystując Memory lub Span.

Jacob Mellor, Dyrektor Technologiczny @ Team Iron
Dyrektor ds. technologii

Jacob Mellor jest Chief Technology Officer w Iron Software i wizjonerskim inżynierem, pionierem technologii C# PDF. Jako pierwotny deweloper głównej bazy kodowej Iron Software, kształtuje architekturę produktów firmy od jej początku, przekształcając ją wspólnie z CEO Cameron Rimington w firmę liczą...

Czytaj więcej

Zespol wsparcia Iron

Jestesmy online 24 godziny, 5 dni w tygodniu.
Czat
Email
Zadzwon do mnie