SpanOwner<T>
Jest SpanOwner<T>
to typ buforu tylko dla stosu, który wynajmuje z puli pamięci udostępnionej. Zasadniczo odzwierciedla funkcjonalność MemoryOwner<T>
elementu , ale jako ref struct
typ. Jest to szczególnie przydatne w przypadku krótkotrwałych, które są używane tylko w kodzie synchronicznym (które nie wymagają Memory<T>
wystąpień), a także w pętli ścisłej, ponieważ tworzenie SpanOwner<T>
wartości w ogóle nie będzie wymagać alokacji pamięci.
Interfejsy API platformy:
SpanOwner<T>
,MemoryOwner<T>
Składnia
Te same podstawowe funkcje MemoryOwner<T>
mają zastosowanie również do tego typu, z wyjątkiem tego, że jest to tylko stos struct
, i fakt, że nie ma IMemoryOwner<T>
interface
implementacji, a także Memory<T>
właściwość. Składnia jest praktycznie identyczna z składnią używaną z MemoryOwner<T>
programem , z wyjątkiem różnic wymienionych powyżej.
Załóżmy na przykład, że mamy metodę, w której musimy przydzielić tymczasowy bufor o określonym rozmiarze (nazwijmy tę wartość length
), a następnie użyjemy jej do wykonania jakiejś pracy. Po pierwsze, nieefektywna wersja może wyglądać następująco:
byte[] buffer = new byte[length];
// Use buffer here
Nie jest to idealne rozwiązanie, ponieważ przydzielamy nowy bufor za każdym razem, gdy ten kod jest używany, a następnie natychmiast go wyrzuca (jak wspomniano również w MemoryOwner<T>
dokumentacji), co wywiera większą presję na moduł odśmiecania pamięci. Możemy zoptymalizować powyższy kod przy użyciu polecenia ArrayPool<T>
:
// Using directive to access the ArrayPool<T> type
using System.Buffers;
int[] buffer = ArrayPool<int>.Shared.Rent(length);
try
{
// Slice the span, as it might be larger than the requested size
Span<int> span = buffer.AsSpan(0, length);
// Use the span here
}
finally
{
ArrayPool<int>.Shared.Return(buffer);
}
Powyższy kod wynajmuje bufor z puli tablic, ale jest bardziej szczegółowy i podatny na błędy: musimy zachować ostrożność z try/finally
blokiem, aby upewnić się, że zawsze zwraca wynajęty bufor do puli. Możemy go ponownie napisać przy użyciu SpanOwner<T>
typu , w następujący sposób:
// Be sure to include this using at the top of the file:
using Microsoft.Toolkit.HighPerformance.Buffers;
using SpanOwner<int> buffer = SpanOwner<int>.Allocate(length);
Span<int> span = buffer.Span;
// Use the span here, no slicing necessary
Wystąpienie SpanOwner<T>
wewnętrznie wynajmuje tablicę i zajmie się zwróceniem jej do puli, gdy wykracza poza zakres. Nie musimy już używać try/finally
bloku, ponieważ kompilator języka C# doda to automatycznie podczas rozszerzania tej using
instrukcji. W związku z tym SpanOwner<T>
typ może być postrzegany jako uproszczona otoka interfejsów ArrayPool<T>
API, co sprawia, że są one zarówno bardziej kompaktowe, jak i łatwiejsze do użycia, zmniejszając ilość kodu, który należy zapisać w celu prawidłowego wynajmu i usuwania krótkotrwałych. Możesz zobaczyć, jak użycie SpanOwner<T>
sprawia, że kod jest znacznie krótszy i prostszy.
Uwaga
Ponieważ jest to typ tylko stosu, opiera się na wzorcu typu duck wprowadzonym IDisposable
w języku C# 8. Pokazano to w powyższym przykładzie: SpanOwner<T>
typ jest używany w using
bloku, pomimo faktu, że typ w ogóle nie implementuje interfejsu IDisposable
i nigdy nie jest w polu. Funkcjonalność jest taka sama: gdy tylko bufor wykracza poza zakres, jest on automatycznie usuwany. Interfejsy API w SpanOwner{T}
programie opierają się na tym wzorcu dla dodatkowej wydajności: zakładają, że bufor bazowy nigdy nie zostanie usunięty, o ile SpanOwner<T>
typ jest w zakresie i nie wykonują dodatkowych kontroli, które są wykonywane w MemoryOwner<T>
celu zapewnienia, że bufor jest w rzeczywistości nadal dostępny przed zwróceniem Memory<T>
wystąpienia lub Span<T>
z niego. W związku z tym ten typ powinien być zawsze używany z blokiem lub wyrażeniem using
. Nie spowoduje to zwrócenia bazowego buforu do puli udostępnionej. Technicznie można to również osiągnąć, ręcznie wywołując Dispose
typ (który nie wymaga języka C# 8), ale jest podatny na SpanOwner<T>
błędy i dlatego nie jest to zalecane.
Przykłady
.NET Community Toolkit