Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O SpanOwner<T>
é um tipo de buffer apenas em pilha que reserva buffers de um pool de memória compartilhada. Ele essencialmente espelha a funcionalidade do MemoryOwner<T>
, mas como um ref struct
tipo. Isso é particularmente útil para buffers de curta vida útil que são usados apenas em código síncrono (que não exigem Memory<T>
instâncias), bem como código executado em um ciclo repetitivo, pois a criação de SpanOwner<T>
valores não exigirá alocações de memória.
APIs da plataforma :
SpanOwner<T>
,MemoryOwner<T>
Sintaxe
As mesmas características principais de MemoryOwner<T>
se aplicam a este tipo também, com a exceção de ser apenas uma struct
de pilha, e o facto de que não possui a implementação de IMemoryOwner<T>
interface
, bem como a propriedade Memory<T>
. A sintaxe é praticamente idêntica à usada com MemoryOwner<T>
, exceto pelas diferenças mencionadas acima.
Como exemplo, suponha que temos um método em que precisamos alocar um buffer temporário de um tamanho especificado (vamos chamar esse valor length
) e, em seguida, usá-lo para executar algum trabalho. Uma primeira versão ineficiente pode ter esta aparência:
byte[] buffer = new byte[length];
// Use buffer here
Isso não é o ideal, pois estamos a alocar um novo buffer cada vez que este código é usado, e depois descartá-lo imediatamente (como mencionado nos documentos MemoryOwner<T>
), 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 try/finally
bloco para garantir que sempre retornem o buffer alugado para o pool. Podemos reescrevê-lo usando o SpanOwner<T>
tipo, assim:
// 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 SpanOwner<T>
instância alugará internamente uma matriz e cuidará de devolvê-la ao pool quando ela sair do escopo. Também não precisamos mais usar um try/finally
bloco, pois o compilador C# adicionará isso automaticamente ao expandir essa using
instrução. Como tal, o tipo SpanOwner<T>
pode ser visto como um envoltório leve em torno das APIs ArrayPool<T>
, tornando-as mais compactas e fáceis de usar, reduzindo a quantidade de código que precisa ser escrito para alugar e descartar corretamente buffers de curta duração. Você pode ver como o uso SpanOwner<T>
torna o código muito mais curto e direto.
Observação
Como este é um tipo exclusivo para pilha, ele depende do padrão de tipagem de pato introduzido no C# 8. Isso é mostrado no exemplo acima: o tipo SpanOwner<T>
está a ser utilizado dentro de um bloco using
, apesar de o tipo não implementar a interface IDisposable
e também nunca ser empacotado. A funcionalidade é exatamente a mesma: assim que o buffer sai do escopo, ele é automaticamente descartado. As APIs em SpanOwner<T>
confiam nesse padrão para obter desempenho extra: assumem que o buffer subjacente nunca será descartado enquanto o tipo SpanOwner<T>
estiver no escopo, e não realizam as verificações adicionais realizadas em MemoryOwner<T>
para garantir que o buffer ainda esteja de fato disponível antes de retornar uma instância de Memory<T>
ou Span<T>
dele. Como tal, este tipo deve ser sempre usado com um using
bloco ou expressão. 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 Dispose
manualmente o SpanOwner<T>
tipo (que não requer C# 8), mas isso é propenso a erros e, portanto, não recomendado.
Exemplos
Pode encontrar mais exemplos nos testes unitários
.NET Community Toolkit