SpanOwner<T>
O SpanOwner<T>
é um tipo de buffer somente pilha que aluga buffers de um pool de memória compartilhado. Essencialmente, ele espelha a funcionalidade de MemoryOwner<T>
, mas como um tipo ref struct
. Isso é útil principalmente para buffers de curta duração que são usados apenas em código síncrono (que não exigem instâncias de Memory<T>
), bem como o código em execução em um loop estreito, pois a criação de valores SpanOwner<T>
não exigirá alocações de memória.
APIs de Plataforma:
SpanOwner<T>
,MemoryOwner<T>
Sintaxe
Os mesmos recursos principais de MemoryOwner<T>
se aplicam a este tipo também, com a exceção de ser um struct
apenas para pilha e do fato de que ele não possui a implementação de IMemoryOwner<T>
interface
, assim como a propriedade Memory<T>
. A sintaxe também é praticamente idêntica à usada com MemoryOwner<T>
, exceto pelas diferenças mencionadas acima.
Por exemplo, suponha que tenhamos um método em que precisamos alocar um buffer temporário de um tamanho especificado (vamos chamar esse valor length
) e usá-lo para executar algum trabalho. Uma primeira versão ineficiente pode ter essa aparência:
byte[] buffer = new byte[length];
// Use buffer here
Isso não é ideal, pois estamos alocando um novo buffer sempre que esse código é usado e, em seguida, jogando-o fora imediatamente (como mencionado nos documentos de MemoryOwner<T>
também), o que coloca mais pressão sobre o coletor de lixo. Podemos otimizar o código acima usando 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);
}
O código acima aluga um buffer de um pool de matrizes, mas é mais detalhado e propenso a erros: precisamos ter cuidado com o bloco try/finally
para sempre retornar o buffer alugado para o pool. Podemos reescrevê-lo usando o tipo SpanOwner<T>
, da seguinte maneira:
// 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
A instância de SpanOwner<T>
alugará internamente uma matriz e vai devolvê-la ao pool quando ela sair do escopo. Também não precisamos mais usar um bloco de try/finally
, pois o compilador C# adicionará isso automaticamente ao expandir essa instrução using
. Dessa forma, o tipo SpanOwner<T>
pode ser visto como um wrapper leve em torno das APIs de ArrayPool<T>
, o que as tornam mais compactas e fáceis de usar, reduzindo a quantidade de código que precisa ser gravado para alugar e descartar buffers de curta duração corretamente. Você pode ver que usar o SpanOwner<T>
torna o código mais curto e simples.
Observação
Como este é um tipo somente de pilha, ele se baseia no padrão de IDisposable
tipo duck introduzido com C# 8. Isso é mostrado no exemplo acima: o tipo SpanOwner<T>
está sendo usado em um bloco using
, apesar do fato de que o tipo não implementa a interface de IDisposable
e também nunca está desmarcado. A funcionalidade é a mesma: assim que o buffer sai do escopo, ele é descartado automaticamente. As APIs no SpanOwner{T}
dependem desse padrão para um desempenho adicional: elas pressupõem que o buffer subjacente nunca será descartado desde que o tipo SpanOwner<T>
esteja no escopo e não executem as verificações adicionais feitas em MemoryOwner<T>
para garantir que o buffer ainda esteja disponível antes de retornar uma instância de Memory<T>
ou Span<T>
dele. Dessa forma, esse tipo sempre deve ser usado com um bloco ou expressão using
. Não fazer isso fará com que o buffer subjacente não seja retornado ao pool compartilhado. Tecnicamente, o mesmo também pode ser alcançado chamando manualmente Dispose
no tipo SpanOwner<T>
(que não requer C# 8), mas isso é propenso a erros e, portanto, não é recomendado.
Exemplos
Você pode encontrar mais exemplos nos testes de unidade.
.NET Community Toolkit