Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.
Advertência
Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e .NET Core. Para a versão atual, consulte a versão .NET 10 deste artigo.
Este artigo explica o ASP.NET processo de eliminação de componentes principais Razor com IDisposable e IAsyncDisposable.
Se um componente implementa IDisposable ou IAsyncDisposable, a estrutura solicita a eliminação de recursos quando o componente é removido da interface do usuário. Não confie no momento exato de quando esses métodos são executados. Por exemplo, IAsyncDisposable pode ser acionado antes ou depois que um Task assíncrono, aguardado em OnInitalizedAsync ou OnParametersSetAsync, seja chamado ou concluído. Além disso, o código de descarte de objetos não deve assumir a existência de objetos criados durante a inicialização ou outros métodos de ciclo de vida.
Os componentes não precisam 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 IAsyncDisposable implementações não demorem muito tempo 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.
Remoção de referências de objetos de interoperabilidade de JavaScript.
Exemplos ao longo dos artigos de interoperabilidade JavaScript (JS) demonstram padrões típicos de eliminação de objetos:
Ao chamar JS do .NET, conforme descrito em chamar funções JavaScript dos métodos .NET no ASP.NET Core Blazor, descarte qualquer IJSObjectReference/IJSInProcessObjectReference/JSObjectReference criado do .NET ou do JS para evitar o vazamento de memória JS.
Ao chamar o .NET do JS, conforme descrito em Chamar métodos .NET de funções JavaScript no ASP.NET Core Blazor, descarte qualquer um criado DotNetObjectReference a partir do .NET ou do para evitar o vazamento de JS memória do .NET.
JS referências de objeto de interoperabilidade são implementadas como um mapa digitado por um identificador no lado da chamada de interoperabilidade de JS que cria a referência. Quando o descarte de objeto é iniciado do lado .NET ou JS, o Blazor remove a entrada do mapa e o objeto pode ser coletado como lixo, contanto que nenhuma outra referência forte ao objeto esteja presente.
No mínimo, elimine sempre os objetos criados no lado do .NET para evitar fuga de memória gerida pelo .NET.
Tarefas de limpeza de DOM durante o descarte de componentes
Para obter mais informações, consulte ASP.NET Core Blazor interoperabilidade do JavaScript (JS interop).
Para obter orientação sobre JSDisconnectedException quando um circuito é desconectado, consulte ASP.NET Interoperabilidade do Core Blazor JavaScript (JS interoperabilidade). Para obter orientações gerais sobre o tratamento de erros de interoperabilidade de JavaScript, consulte a seção interoperabilidade de JavaScript em Manipular erros em aplicativos ASP.NET Core Blazor.
Síncrono IDisposable
Para tarefas de eliminação síncronas, use IDisposable.Dispose.
O seguinte componente:
- Implementa IDisposable através da diretiva
@implementsRazor. - Elimina
obj, que é um tipo que implementa IDisposable. - Uma verificação 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 for necessária a libertação de um único objeto, uma lambda pode ser usada para liberar o objeto quando Dispose for invocado. O exemplo a seguir aparece no artigo de renderização do componente ASP.NET Core Razor 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();
}
Observação
No exemplo anterior, a chamada para StateHasChanged é encapsulada por uma chamada para ComponentBase.InvokeAsync porque o retorno de chamada é invocado fora do contexto de sincronização do Blazor. Para obter mais informações, consulte Razorde renderização de componentes do Core .
Se o objeto for criado em um método de ciclo de vida, como OnInitialized{Async}, verifique se há 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 mais informações, consulte:
IAsyncDisposable assíncrono
Para tarefas de eliminação assíncronas, use IAsyncDisposable.DisposeAsync.
O seguinte componente:
- Implementa IAsyncDisposable através da diretiva
@implementsRazor. - Elimina
obj, que é um tipo não gerenciado que implementa IAsyncDisposable. - Uma verificação 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 mais informações, consulte:
- Blazor de contexto de sincronização do Core
- Limpeza de recursos não gerenciados (documentação .NET)
- Operadores condicionais nulos?. e ? []
Atribuição de null a objetos eliminados
Normalmente, não há necessidade de atribuir null a objetos descartados depois de chamar Dispose/DisposeAsync. Os casos raros de atribuição de null incluem o seguinte:
- Se o tipo do objeto estiver mal implementado e não suportar chamadas repetidas para Dispose/DisposeAsync, atribua
nullapós a eliminação para ignorar graciosamente outras chamadas para Dispose/DisposeAsync. - Se um processo de longa duração continuar a conter uma referência a um objeto descartado, a atribuição de
nullpermite que o coletor de lixo liberte o objeto, apesar do processo de longa duração manter uma referência a ele.
São cenários inusitados. Para objetos que são implementados corretamente e se comportam normalmente, não há sentido em atribuir null a objetos descartados. Nos raros casos em que um objeto deve ser atribuído null, recomendamos documentar o motivo e buscar uma solução que evite a necessidade de atribuir null.
StateHasChanged
Observação
Não há suporte para chamadas StateHasChanged no Dispose e DisposeAsync.
StateHasChanged pode ser invocado como parte da destruição do renderizador, portanto, não há suporte para solicitar atualizações da interface do usuário nesse ponto.
Manipuladores de eventos
Sempre cancele a assinatura de manipuladores de eventos .NET. Os exemplos a seguir de formulários Blazor mostram como remover 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;
}
}
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 EditForm e os formulários, consulte a visão geral dos formulários do ASP.NET Core em Blazor e os outros artigos de formulários no nó Forms.
Funções, métodos e expressões anónimas
Quando funções anônimas, métodos ou expressões são usados, não é necessário implementar IDisposable e cancelar a inscrição de delegados. No entanto, não conseguir cancelar a inscrição de um delegado é um problema quando o objeto que expõe o evento sobrevive ao tempo de vida do componente que registra o delegado. Quando isso ocorre, ocorre um vazamento de memória porque o delegado registrado mantém o objeto original vivo. Portanto, use as seguintes abordagens apenas quando souber que o delegado de evento se desfaz rapidamente. Em caso de dúvida sobre a duração dos objetos que exigem ser descartados, faça a subscrição de um método delegado e descarte corretamente o delegado, como mostram os exemplos anteriores.
Método lambda anónimo (eliminação explícita não necessária):
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 de expressão lambda anónima (eliminação explícita não necessária):
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 forms validation.
Para obter mais informações, consulte Limpeza de recursos não gerenciados e os tópicos que se seguem sobre a implementação dos métodos Dispose e DisposeAsync.
Eliminação durante o JS de interoperabilidade
Trave JSDisconnectedException em casos potenciais em que a perda do circuito de Blazor de SignalRimpede as chamadas de interoperação de JS, resultando numa exceção não tratada.
Para obter mais informações, consulte os seguintes recursos: