Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Nota
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão do .NET 10 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão do .NET 10 deste artigo.
Este artigo explica o processo de descarte de componentes do ASP.NET Core Razor com IDisposable e IAsyncDisposable.
Se um componente implementar IDisposable ou IAsyncDisposable, a estrutura solicitará o descarte de recursos quando o componente for removido da interface do usuário. Não confie no tempo exato de quando esses métodos são executados. Por exemplo, IAsyncDisposable pode ser disparado antes ou depois que um Task assíncrono que é aguardado em OnInitalizedAsync ou OnParametersSetAsync seja chamado ou concluído. Além disso, o código de descarte de objetos não deve assumir que os objetos criados durante a inicialização ou outros métodos de ciclo de vida existem.
Os componentes não devem precisar implementar IDisposable e IAsyncDisposable simultaneamente. Se ambos forem implementados, a estrutura executará apenas a sobrecarga assíncrona.
O código do desenvolvedor deve garantir que as implementações de IAsyncDisposable não demorem muito para serem concluídas.
Para obter mais informações, consulte as observações introdutórias do contexto de sincronização do ASP.NET Core Blazor.
Descarte de referências de objeto de interoperabilidade do JavaScript
Os exemplos em todos os artigos de interoperabilidade do JavaScript (JS) demonstram padrões típicos de descarte de objetos:
Ao chamar JS a partir do .NET, conforme descrito em Chamar funções JavaScript a partir de métodos do .NET no Blazor do ASP.NET Core, descarte qualquer IJSObjectReference/IJSInProcessObjectReference/JSObjectReference criado a partir do .NET ou do JS para evitar perda de memória JS.
Ao chamar o .NET de JS, conforme descrito em métodos .NET de chamadas de funções JavaScript no ASP.NET Core Blazor, descarte qualquer um criado DotNetObjectReference do .NET ou de modo a evitar o vazamento de JS memória do .NET.
JS As referências de objeto de interoperabilidade são implementadas como um mapa com chave de um identificador no lado da chamada de interoperabilidade JS que cria a referência. Quando o descarte de objetos é iniciado do lado do .NET ou JS, Blazor remove a entrada do mapa e o objeto pode ser coletado como lixo contanto que não haja nenhuma outra referência forte ao objeto.
No mínimo, sempre descarte objetos criados no lado do .NET para evitar o vazamento de memória gerenciada do .NET.
Tarefas de limpeza do DOM durante o descarte de componentes
Para obter mais informações, confira Interoperabilidade ASP.NET Core Blazor JavaScript (interoperabilidade JS).
Para obter diretrizes sobre JSDisconnectedException quando um circuito é desconectado, consulte Interoperabilidade de JavaScript no ASP.NET CoreBlazor (JS interoperabilidade). Para obter diretrizes gerais sobre tratamento de erros de interoperabilidade do JavaScript, confira a seção Interoperabilidade do JavaScript em Tratar erros em aplicativos Blazor do ASP.NET Core.
IDisposable síncrono
Para operações de descarte sincrônicas, use IDisposable.Dispose.
O seguinte componente :
- Implementa IDisposable com a diretiva
@implementsRazor. - Descarta
obj, que é um tipo que implementa IDisposable. - Uma marcar nula é executada porque
objé criado em um método de ciclo de vida (não mostrado).
@implements IDisposable
...
@code {
...
public void Dispose()
{
obj?.Dispose();
}
}
Se um único objeto exigir descarte, um lambda poderá ser usado para descartar o objeto quando Dispose for chamado. O exemplo a seguir é mostrado no artigo Renderização de componentes Razor do ASP.NET Core e demonstra o uso de uma expressão lambda para o descarte de um Timer.
TimerDisposal1.razor:
@page "/timer-disposal-1"
@using System.Timers
@implements IDisposable
<PageTitle>Timer Disposal 1</PageTitle>
<h1>Timer Disposal Example 1</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
TimerDisposal1.razor:
@page "/timer-disposal-1"
@using System.Timers
@implements IDisposable
<PageTitle>Timer Disposal 1</PageTitle>
<h1>Timer Disposal Example 1</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
CounterWithTimerDisposal1.razor:
@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
CounterWithTimerDisposal1.razor:
@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
CounterWithTimerDisposal1.razor:
@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
CounterWithTimerDisposal1.razor:
@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new Timer(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
Nota
No exemplo anterior, a chamada para StateHasChanged é encapsulada por uma chamada para ComponentBase.InvokeAsync porque o callback é invocado fora do contexto de sincronização de Blazor. Para saber mais, consulte Renderização de componentes de Razor no ASP.NET Core.
Se o objeto for criado em um método de ciclo de vida, como OnInitialized{Async}, verifique null antes de chamar Dispose.
TimerDisposal2.razor:
@page "/timer-disposal-2"
@using System.Timers
@implements IDisposable
<PageTitle>Timer Disposal 2</PageTitle>
<h1>Timer Disposal Example 2</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer? timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
TimerDisposal2.razor:
@page "/timer-disposal-2"
@using System.Timers
@implements IDisposable
<PageTitle>Timer Disposal 2</PageTitle>
<h1>Timer Disposal Example 2</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer? timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
CounterWithTimerDisposal2.razor:
@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer? timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
CounterWithTimerDisposal2.razor:
@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer? timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
CounterWithTimerDisposal2.razor:
@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
CounterWithTimerDisposal2.razor:
@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
Para obter mais informações, consulte:
IAsyncDisposable assíncrono
Para tarefas de eliminação assíncronas, use IAsyncDisposable.DisposeAsync.
O seguinte componente :
- Implementa IAsyncDisposable com a diretiva
@implementsRazor. - Descarta
obj, que é um tipo não gerenciado que implementa IAsyncDisposable. - Uma marcar nula é executada porque
objé criado em um método de ciclo de vida (não mostrado).
@implements IAsyncDisposable
...
@code {
...
public async ValueTask DisposeAsync()
{
if (obj is not null)
{
await obj.DisposeAsync();
}
}
}
Para obter mais informações, consulte:
- Contexto de sincronização Blazor do ASP.NET Core
- Limpar recursos não gerenciados (documentação do .NET)
- Operadores condicionais nulos ?. e ?[]
Atribuição de null aos objetos descartados
Normalmente, não é necessário atribuir null a objetos descartados depois de chamar Dispose/DisposeAsync. Casos raros para atribuir null incluem o seguinte:
- Se o tipo do objeto for implementado incorretamente e não tolerar chamadas repetidas para Dispose/DisposeAsync, atribua
nullapós o descarte para ignorar normalmente outras chamadas para Dispose/DisposeAsync. - Se um processo de longa duração continuar mantendo uma referência a um objeto descartado, atribuir
nullpermitirá que o coletor de lixo libere o objeto, apesar de o processo de longa duração manter uma referência a ele.
São cenários incomuns. Para objetos que são implementados corretamente e se comportam normalmente, não faz sentido atribuir null a objetos descartados. Nos casos raros em que um objeto deve ser atribuído null, recomendamos documentar o motivo e buscar uma solução que impeça a necessidade de atribuir null.
StateHasChanged
Nota
Não há suporte para chamar StateHasChanged em Dispose e DisposeAsync.
StateHasChanged pode ser invocado como parte da demolição do renderizador, portanto, não há suporte para a solicitação de atualizações da interface do usuário nesse ponto.
Manipuladores de eventos
Sempre cancele a assinatura dos manipuladores de eventos nos eventos do .NET. Os seguintes exemplos de Blazor formulário mostram como cancelar a assinatura de um manipulador de eventos no método Dispose.
Campo privado e abordagem lambda:
@implements IDisposable
<EditForm ... EditContext="editContext" ...>
...
<button type="submit" disabled="@formInvalid">Submit</button>
</EditForm>
@code {
...
private EventHandler<FieldChangedEventArgs>? fieldChanged;
protected override void OnInitialized()
{
editContext = new(model);
fieldChanged = (_, __) =>
{
...
};
editContext.OnFieldChanged += fieldChanged;
}
public void Dispose()
{
editContext.OnFieldChanged -= fieldChanged;
}
}
Abordagem de método privado:
@implements IDisposable
<EditForm ... EditContext="editContext" ...>
...
<button type="submit" disabled="@formInvalid">Submit</button>
</EditForm>
@code {
...
protected override void OnInitialized()
{
editContext = new(model);
editContext.OnFieldChanged += HandleFieldChanged;
}
private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
{
...
}
public void Dispose()
{
editContext.OnFieldChanged -= HandleFieldChanged;
}
}
Para obter mais informações sobre o componente e os formulários do EditForm, consulte Visão geral dos formulários do ASP.NET Core Blazor e os outros artigos de formulários no nó Forms.
Funções anônimas, métodos e expressões
Quando funções anônimas, métodos ou expressões são usados, não é necessário implementar IDisposable e cancelar a assinatura de delegados. No entanto, não assinar um delegado é um problema quando o objeto que expõe o evento ultrapassa o tempo de vida do componente que registra o delegado. Quando isso ocorre, um vazamento de memória resulta porque o delegado registrado mantém o objeto original ativo. Portanto, use apenas as abordagens a seguir quando souber que o delegado do evento será descartado rapidamente. Quando estiver em dúvida sobre o tempo de vida dos objetos que exigem descarte, assine um método delegado e descarte corretamente o delegado, como mostram os exemplos anteriores.
Abordagem anônima do método lambda (descarte explícito não necessário):
private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
{
formInvalid = !editContext.Validate();
StateHasChanged();
}
protected override void OnInitialized()
{
editContext = new(starship);
editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e);
}
Abordagem anônima da expressão lambda (descarte explícito não necessário):
private ValidationMessageStore? messageStore;
[CascadingParameter]
private EditContext? CurrentEditContext { get; set; }
protected override void OnInitialized()
{
...
messageStore = new(CurrentEditContext);
CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear();
CurrentEditContext.OnFieldChanged += (s, e) =>
messageStore.Clear(e.FieldIdentifier);
}
O exemplo completo do código anterior com expressões lambda anônimas aparece no artigo ASP.NET Core Blazor validação de formulários.
Para obter mais informações, consulte Limpeza de recursos não gerenciados e os tópicos que o seguem na implementação dos métodos Dispose e DisposeAsync.
Descarte durante a interoperabilidade JS
Intercepção de JSDisconnectedException em casos potenciais onde a perda do circuito BlazorSignalR impede chamadas interop JS e resulta em uma exceção não tratada.
Para obter mais informações, consulte os seguintes recursos: