SpanOwner<T>
SpanOwner<T>
constitue un type de tampon à pile uniquement qui loue des tampons à partir d’un pool de mémoire partagé. Il met essentiellement en mémoire la fonctionnalité de MemoryOwner<T>
, mais comme type ref struct
. Ce type d’opération est particulièrement utile pour des mémoires de courte durée uniquement utilisées dans du code synchrone (qui ne nécessitent aucune instance Memory<T>
), ainsi que dans du code s’exécutant dans une boucle serrée, comme la création de valeurs SpanOwner<T>
qui n’exigent aucune allocation de mémoire.
API de plateforme :
SpanOwner<T>
,MemoryOwner<T>
Syntaxe
Les mêmes fonctionnalités de base de MemoryOwner<T>
s’appliquent également à ce type, excepté s’il s’agit d’un struct
de pile uniquement et si l’implémentation de l’interface
IMemoryOwner<T>
y est manquante, ainsi que la propriété Memory<T>
. La syntaxe est virtuellement identique à ce qui est également utilisé avec MemoryOwner<T>
, à l’exception des différences précédemment mentionnées.
Par exemple, supposons que nous avons une ,méthode dans laquelle nous devons allouer un tampon temporaire de taille spécifiée (appelons cette valeur length
) que nous utilisons ensuite pour effectuer certains travaux. Il est possible qu’une première version inefficace ressemble à ceci :
byte[] buffer = new byte[length];
// Use buffer here
Cette opération n’est pas optimale, car nous allouons un nouveau tampon lors de chaque utilisation de ce code, puis nous le supprimons immédiatement (comme indiqué également dans la documentation MemoryOwner<T>
), ce qui crée davantage de pression sur le récupérateur de mémoire. Nous pouvons optimiser le code ci-dessus en utilisant 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);
}
Le code ci-dessus loue un tampon d’un pool de tableaux, mais il est plus détaillé et générateur d’erreurs : nous devons être prudents avec le bloc try/finally
pour veiller à toujours retourner le tampon loué au pool. Nous pouvons le réécrire en utilisant le type SpanOwner<T>
, comme suit :
// 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
L’instance SpanOwner<T>
va louer un tableau en interne et s’occuper de le retourner au pool lorsqu’il est hors de portée. Nous n’avons plus besoin d’utiliser un bloc try/finally
, car le compiler C# va l’ajouter automatiquement lors du développement de cette instruction using
. Ainsi, le type SpanOwner<T>
peut être considéré comme un wrapper léger autour des API ArrayPool<T>
, ce qui les rend plus compactes et faciles à utiliser et réduit ainsi la quantité de code nécessaire à écrire pour louer et supprimer correctement des mémoires de courte durée. Vous pouvez voir comment l’utilisation de SpanOwner<T>
rend le code beaucoup plus court et plus direct.
Remarque
Étant donné qu’il s’agit d’un type à pile uniquement, il s’appuie sur le modèle IDisposable
duck-typed (à typage canard) présenté dans C# 8. Il est présenté dans l’exemple ci-dessus : le type SpanOwner<T>
est utilisé au sein d’un bloc using
malgré le fait que le type n’implémente pas du tout l’interface IDisposable
et qu’il ne soit jamais encadré. La fonctionnalité est simplement la même : dès que le tampon est hors de portée, il est automatiquement supprimé. Les API dans SpanOwner{T}
s’appuient sur ce modèle pour bénéficier de performances supplémentaires : elles supposent que le tampon sous-jacent ne va jamais être supprimé aussi longtemps que le type SpanOwner<T>
reste dans l’étendue. Elles n’effectuent pas les vérifications supplémentaires réalisées dans MemoryOwner<T>
pour veiller à ce que le tampon soit toujours disponible avant d’y retourner une Memory<T>
ou une instance Span<T>
. Ainsi, ce type doit toujours être utilisé avec une expression ou un bloc using
. Si vous n’effectuez pas cette action, le tampon sous-jacent ne sera pas retourné au pool partagé. Vous pouvez techniquement effectuer la même opération en appelant manuellement Dispose
sur le type SpanOwner<T>
(qui ne nécessite pas C# 8), mais cette action est sujette à erreurs et donc déconseillée.
Exemples
Vous trouverez d’autres exemples dans les tests unitaires.
.NET Community Toolkit