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
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
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
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'
3. Przekazywanie podciagu do metody
Przekaż ReadOnlySpan
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))
4. Wyszukiwanie w String
ReadOnlySpanIndexOf().
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
5. Użycie z plikami mapowanymi w pamieci
ReadOnlySpan
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
6. Efektywna manipulacja napisami
ReadOnlySpan
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
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))
ReadOnlySpan
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
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
W tym przykładzie:
Marshal.AllocHGlobalalokuje pamięć niezarządzaną dla danych docelowych.new Span<int>(destinationPointer.ToPointer(), sourceArray.Length)tworzySpan<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
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
W tym przykladzie metoda ReadAsync asynchronicznie odczytuje dane z strumienia do buforu. Metoda ProcessData następnie przetwarza dane bezpośrednio z Span
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
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
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
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

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
W tym przykładzie używamy Span w połączeniu z IronPDF, aby generować dokumenty PDF.
Wynik:

Wygenerowany 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.




