SpanOwner<T>
SpanOwner<T>
ist ein puffergeschützter Puffertyp, der Puffer aus einem freigegebenen Speicherpool ausleiht. Es spiegelt im Wesentlichen die Funktionalität von MemoryOwner<T>
wider, aber als ref struct
-Typ. Dies ist besonders nützlich für kurzlebige Puffer, die nur in synchronem Code verwendet werden (die keine Memory<T>
-Instanzen erfordern), sowie Code, der in einer engen Schleife ausgeführt wird, da das Erstellen von SpanOwner<T>
-Werten überhaupt keine Speicherzuweisungen erfordert.
Plattform-APIs:
SpanOwner<T>
,MemoryOwner<T>
Syntax
Die gleichen Kernfeatures von MemoryOwner<T>
gelten auch für diesen Typ, mit Ausnahme davon, dass es sich um eine Stack-Only-struct
handelt, und der Tatsache, dass die IMemoryOwner<T>
interface
-Implementierung sowie die Memory<T>
-Eigenschaft fehlen. Die Syntax ist praktisch identisch mit MemoryOwner<T>
, mit Ausnahme der oben genannten Unterschiede.
Nehmen wir beispielsweise an, wir haben eine Methode, bei der wir einen temporären Puffer einer angegebenen Größe zuweisen müssen (rufen wir diesen Wert length
auf), und verwenden Sie sie dann, um einige Aufgaben auszuführen. Eine erste, ineffiziente Version könnte wie folgt aussehen:
byte[] buffer = new byte[length];
// Use buffer here
Dies ist nicht ideal, da wir jedes Mal, wenn dieser Code verwendet wird, einen neuen Puffer zuweisen und es dann sofort wegwerfen (wie auch in den MemoryOwner<T>
-Dokumenten erwähnt), wodurch mehr Druck auf den Garbage Collector entsteht. Wir können den obigen Code mithilfe von ArrayPool<T>
optimieren:
// 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);
}
Der obige Code mietet einen Puffer aus einem Arraypool, ist aber ausführlicher und fehleranfälliger: Wir müssen mit dem try/finally
-Block vorsichtig sein, um sicherzustellen, dass der gemietete Puffer immer an den Pool zurückgegeben wird. Wir können ihn mithilfe des SpanOwner<T>
-Typs wie folgt umschreiben:
// 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
Die SpanOwner<T>
-Instanz wird intern ein Array mieten und kümmert sich um die Rückgabe an den Pool, wenn sie außerhalb des Gültigkeitsbereichs ist. Ein try/finally
-Block muss nicht mehr verwendet werden, da der C#-Compiler dies automatisch beim Erweitern dieser using
-Anweisung hinzusetzt. Als solcher kann der SpanOwner<T>
-Typ als einfacher Wrapper um die ArrayPool<T>
-APIs betrachtet werden, wodurch sie sowohl kompakter als auch einfacher zu verwenden sind, wodurch die Menge an Code reduziert wird, der in die ordnungsgemäße Miete geschrieben werden muss und kurzlebige Puffer verworfen werden muss. Sie können sehen, wie die Verwendung von SpanOwner<T>
den Codes viel kürzer und einfacher macht.
Hinweis
Da es sich um einen Nur-Stack-Typ handelt, basiert er auf dem mit C# 8 eingeführten Duck-Typ-IDisposable
-Muster. Dies wird im obigen Beispiel gezeigt: Der SpanOwner<T>
-Typ wird in einem using
-Block verwendet, obwohl der Typ überhaupt nicht die IDisposable
-Schnittstelle implementiert, und es wird auch nie geboxt. Die Funktionalität ist identisch: Sobald der Puffer nicht mehr im Gültigkeitsbereich liegt, wird er automatisch gelöscht. Die APIs in SpanOwner{T}
verlassen sich auf dieses Muster für eine zusätzliche Leistung: Sie gehen davon aus, dass der zugrunde liegende Puffer niemals verworfen wird, solange der SpanOwner<T>
-Typ im Bereich ist, und sie führen nicht die zusätzlichen Prüfungen durch, die in MemoryOwner<T>
ausgeführt werden, um sicherzustellen, dass der Puffer tatsächlich noch verfügbar ist, bevor eine Memory<T>
-Instanz oder eine Span<T>
-Instanz zurückgegeben wird. Daher sollte dieser Typ immer mit einem using
-Block oder Ausdruck verwendet werden. Dies führt dazu, dass der zugrunde liegende Puffer nicht an den freigegebenen Pool zurückgegeben wird. Technisch kann dasselbe auch durch manuelles Aufrufen von Dispose
des SpanOwner<T>
-Typs erfolgen (was C# 8 erfordert), aber das ist fehleranfällig und daher nicht empfohlen.
Beispiele
Weitere Beispiele finden Sie in den Komponententests.
.NET Community Toolkit