Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Note
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 10 de cet article.
Avertissement
Cette version d'ASP.NET Core n'est plus prise en charge. Pour plus d’informations, consultez la stratégie de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 10 de cet article.
Cet article explique le processus d’élimination des composants ASP.NET Core Razor avec IDisposable et IAsyncDisposable.
Si un composant implémente IDisposable ou IAsyncDisposable, l’infrastructure appelle la suppression des ressources lorsque le composant est supprimé de l’interface utilisateur. Ne vous fiez pas au moment exact de l’exécution de ces méthodes. Par exemple, IAsyncDisposable vous pouvez être déclenché avant ou après une attente asynchrone Task dans OnInitalizedAsync ou OnParametersSetAsync est appelée ou terminée. En outre, le code de suppression d’objets ne doit pas supposer que les objets créés pendant l’initialisation ou d’autres méthodes de cycle de vie existent.
Les composants ne doivent pas avoir besoin d’implémenter IDisposable et IAsyncDisposable simultanément. Si les deux sont implémentés, le framework exécute uniquement la surcharge asynchrone.
Le code du développeur doit s’assurer que IAsyncDisposable les implémentations ne prennent pas beaucoup de temps.
Pour plus d’informations, consultez les remarques d’introduction de ASP.NET contexte de synchronisation coreBlazor.
Suppression des références d’objets d’interopérabilité JavaScript
Des exemples dans les articles d’interopérabilité JavaScript (JS) illustrent des modèles de suppression d’objets classiques :
Lors de l’appel JS à partir de .NET, comme décrit dans les fonctions JavaScript d’appel à partir de méthodes .NET dans ASP.NET CoreBlazor, supprimez les éléments créés IJSObjectReference/IJSInProcessObjectReference/JSObjectReferenceà partir de .NET ou pour JS éviter la fuite de JS mémoire.
Lors de l’appel de .NET à partir de JS, comme décrit dans Appeler des méthodes .NET à partir de fonctions JavaScript dans ASP.NET Core Blazor, supprimez les éléments créés DotNetObjectReference à partir de .NET ou pour JS éviter la fuite de mémoire .NET.
JS Les références d’objet d’interopérabilité sont implémentées en tant que carte clé par un identificateur du côté de l’appel JS d’interopérabilité qui crée la référence. Lorsque l’élimination de l’objet est lancée à partir du .NET ou JS du côté, Blazor supprime l’entrée de la carte et que l’objet peut être récupéré à la mémoire tant qu’aucune autre référence forte à l’objet n’est présente.
Au minimum, supprimez toujours les objets créés côté .NET pour éviter la fuite de mémoire managée .NET.
Tâches de nettoyage DOM lors de l’élimination des composants
Pour plus d’informations, consultez Interopérabilité JavaScript d'ASP.NET Core Blazor (interop JS).
Pour obtenir des conseils sur JSDisconnectedException la déconnexion d’un circuit, consultez ASP.NET interopérabilité JavaScript principale Blazor (JS interopérabilité) . Pour obtenir des conseils généraux sur la gestion des erreurs d’interopérabilité JavaScript, consultez la section Interopérabilité JavaScriptdans Gérer les erreurs dans les applications ASP.NET CoreBlazor.
Synchrone IDisposable
Pour les tâches de suppression synchrone, utilisez IDisposable.Dispose.
Le composant suivant :
- Implémente IDisposable avec la
@implementsRazor directive. -
objSupprime , qui est un type qui implémente IDisposable. - Une vérification null est effectuée, car
objelle est créée dans une méthode de cycle de vie (non affichée).
@implements IDisposable
...
@code {
...
public void Dispose()
{
obj?.Dispose();
}
}
Si un objet unique nécessite une suppression, une lambda peut être utilisée pour supprimer l’objet lorsqu’il Dispose est appelé. L’exemple suivant s’affiche dans l’article de rendu des composants ASP.NET Core Razor et illustre l’utilisation d’une expression lambda pour l’élimination d’un 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();
}
Note
Dans l’exemple précédent, l’appel à est StateHasChanged encapsulé par un appel, ComponentBase.InvokeAsync car le rappel est appelé en dehors du contexte de Blazorsynchronisation. Pour plus d’informations, consultez ASP.NET rendu des composants principauxRazor.
Si l’objet est créé dans une méthode de cycle de vie, par OnInitialized{Async}exemple, vérifiez avant null d’appeler 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();
}
Pour plus d’informations, consultez :
Asynchrone IAsyncDisposable
Pour les tâches d’élimination asynchrones, utilisez IAsyncDisposable.DisposeAsync.
Le composant suivant :
- Implémente IAsyncDisposable avec la
@implementsRazor directive. -
objSupprime , qui est un type non managé qui implémente IAsyncDisposable. - Une vérification null est effectuée, car
objelle est créée dans une méthode de cycle de vie (non affichée).
@implements IAsyncDisposable
...
@code {
...
public async ValueTask DisposeAsync()
{
if (obj is not null)
{
await obj.DisposeAsync();
}
}
}
Pour plus d’informations, consultez :
- contexte de synchronisation ASP.NET Principal Blazor
- Nettoyage des ressources non managées (documentation .NET)
- Opérateurs conditionnels Null ?. et? []
Affectation d’objets null supprimés
En règle générale, il n’est pas nécessaire d’affecter null des objets supprimés après l’appelDispose/DisposeAsync. Les cas rares pour l’affectation null incluent les éléments suivants :
- Si le type de l’objet est mal implémenté et ne tolère pas les appels répétés, Dispose/DisposeAsyncaffectez
nullaprès suppression pour ignorer correctement les appels à .Dispose/DisposeAsync - Si un processus de longue durée continue de contenir une référence à un objet supprimé, l’affectation
nullpermet au garbage collector de libérer l’objet malgré le processus de longue durée contenant une référence à celui-ci.
Il s’agit de scénarios inhabituels. Pour les objets qui sont implémentés correctement et se comportent normalement, il n’existe aucun point d’affectation null aux objets supprimés. Dans les rares cas où un objet doit être affecté null, nous vous recommandons de documenter la raison et de rechercher une solution qui empêche l’affectation de l’objet null.
StateHasChanged
Note
L’appel StateHasChanged n’est pas pris en DisposeDisposeAsync charge.
StateHasChanged peut être appelé dans le cadre de la destruction du renderer, de sorte que la demande de mises à jour de l’interface utilisateur à ce stade n’est pas prise en charge.
Gestionnaires d’événements
Désinscrivez toujours les gestionnaires d’événements à partir d’événements .NET. Les exemples de formulaires suivants Blazor montrent comment désabonner un gestionnaire d’événements dans la Dispose méthode.
Champ privé et approche 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;
}
}
Approche de méthode privée :
@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;
}
}
Pour plus d’informations sur le EditForm composant et les formulaires, consultez ASP.NET Vue d’ensemble des formulaires principaux Blazor et les autres articles sur les formulaires dans le nœud Formulaires.
Fonctions anonymes, méthodes et expressions
Lorsque des fonctions, méthodes ou expressions anonymes sont utilisées, il n’est pas nécessaire d’implémenter IDisposable et de désabonner des délégués. Toutefois, l’échec de l’annulation d’un délégué est un problème lorsque l’objet exposant l’événement dépasse la durée de vie du composant qui inscrit le délégué. Lorsque cela se produit, une fuite de mémoire se produit, car le délégué inscrit conserve l’objet d’origine actif. Par conséquent, utilisez uniquement les approches suivantes lorsque vous savez que le délégué d’événement se supprime rapidement. En cas de doute sur la durée de vie des objets qui nécessitent une suppression, abonnez-vous à une méthode de délégué et supprimez correctement le délégué comme le montrent les exemples précédents.
Approche de méthode lambda anonyme (suppression explicite non requise) :
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);
}
Approche d’expression lambda anonyme (suppression explicite non requise) :
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);
}
L’exemple complet du code précédent avec des expressions lambda anonymes apparaît dans l’article de validation des formulaires core Blazor ASP.NET .
Pour plus d’informations, consultez Nettoyage des ressources non managées et des rubriques qui suivent sur l’implémentation des méthodes et Dispose des DisposeAsync méthodes.
Élimination pendant l’interopérabilité JS
Interceptez JSDisconnectedException dans les cas potentiels où la perte du BlazorSignalR circuit empêche JS les appels d’interopérabilité et génère une exception non gérée.
Pour plus d’informations, consultez les ressources suivantes :