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 informações sobre a versão vigente, confira a versão .NET 9 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 informações sobre a versão vigente, confira a versão .NET 9 deste artigo.
Importante
Essas informações estão relacionadas a um produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado comercialmente. A Microsoft não oferece garantias, expressas ou implícitas, em relação às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
Este artigo explica o Razor descarte de componentes do ASP.NET Core 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 a partir do JS, conforme descrito em Chamar métodos do .NET a partir das funções JavaScript no Blazor do ASP.NET Core, descarte um DotNetObjectReference criado a partir do .NET ou do JS para evitar perda de 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
@implements
Razor. - 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
@implements
Razor. - 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
null
apó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
null
permitirá 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: