Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Not
Det här är inte den senaste versionen av den här artikeln. Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.
Varning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.
Viktig
Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.
För den aktuella versionen, se artikelns .NET 9-version.
I den här artikeln beskrivs ASP.NET Core Razor-komponentens bortskaffande med IDisposable och IAsyncDisposable.
Om en komponent implementerar IDisposable eller IAsyncDisposableanropar ramverket bortskaffande av resurser när komponenten tas bort från användargränssnittet. Förlita dig inte på den exakta tidpunkten för när dessa metoder körs. Till exempel kan IAsyncDisposable utlösas före eller efter att en asynkron Task väntas på i OnInitalizedAsync
eller att OnParametersSetAsync
anropas eller slutförs. Dessutom bör objekthanteringskoden inte förutsätta att objekt som skapats under initieringen eller andra livscykelmetoder finns.
Komponenter ska inte behöva implementera IDisposable och IAsyncDisposable samtidigt. Om båda implementeras kör ramverket bara den asynkrona överbelastningen.
Utvecklarkod måste se till att IAsyncDisposable implementeringar inte tar lång tid att slutföra.
Mer information finns i introduktionskommentarerna för ASP.NET Core Blazor synkroniseringskontext.
Borttagning av javascript-interopobjektreferenser
Exempel i JavaScript -JS) interop-artiklar demonstrera typiska mönster för bortskaffande av objekt:
När du anropar JS från .NET, enligt beskrivningen i Anropa JavaScript-funktioner från .NET-metoder i ASP.NET Core Blazor, kan du ta bort alla skapade IJSObjectReference/IJSInProcessObjectReference/JSObjectReference antingen från .NET eller från JS för att undvika att läcka JS minne.
När du anropar .NET från JS, enligt beskrivningen i Anropa .NET-metoder från JavaScript-funktioner i ASP.NET Core Blazor, gör du dig av med en skapad DotNetObjectReference antingen från .NET eller från JS för att undvika läckage av .NET-minne.
JS interop-objektreferenser implementeras som en karta som styrs av en identifierare på sidan av JS interop-anrop som skapar referensen. När bortskaffande av objekt initieras från .NET- eller JS-sidan tar Blazor bort posten från kartan, och objektet kan vara skräp som samlas in så länge ingen annan stark referens till objektet finns.
Ta alltid bort objekt som skapats på .NET-sidan för att undvika att läcka .NET-hanterat minne.
DOM-rensningsuppgifter vid bortskaffande av komponenter
Mer information finns i ASP.NET Core Blazor JavaScript-samverkan (JS interop).
Mer information om JSDisconnectedException när en krets är frånkopplad finns i ASP.NET Core Blazor JavaScript-samverkan (JS interop). Allmän vägledning för JavaScript-interopfelhantering finns i avsnittet JavaScript interop i Hantera fel i ASP.NET Core Blazor-appar.
Synkrona IDisposable
För synkrona borttagningsuppgifter använder du IDisposable.Dispose.
Följande komponent:
- Implementerar IDisposable med
@implements
Razor-direktivet. - Gör sig av med
obj
, som är en typ som implementerar IDisposable. - En null-kontroll utförs eftersom
obj
skapas i en livscykelmetod (visas inte).
@implements IDisposable
...
@code {
...
public void Dispose()
{
obj?.Dispose();
}
}
Om ett enskilt objekt kräver bortskaffande kan en lambda användas för att ta bort objektet när Dispose anropas. Följande exempel visas i artikeln ASP.NET Core Razor-komponentåtergivning och visar användningen av ett lambda-uttryck för bortskaffande av en 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();
}
Not
I föregående exempel omsluts anropet till StateHasChanged av ett anrop till ComponentBase.InvokeAsync eftersom återanropet anropas utanför Blazorsynkroniseringskontext. För mer information, se ASP.NET Core Razor komponentåtergivning.
Om objektet skapas i en livscykelmetod, till exempel OnInitialized{Async}
, kontrollerar du null
innan du anropar 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();
}
Mer information finns i:
Asynkron IAsyncDisposable
För asynkrona borttagningstjänster använd IAsyncDisposable.DisposeAsync.
Följande komponent:
- Implementerar IAsyncDisposable med
@implements
Razor-direktivet. - Tar bort
obj
, som är en ohanterad typ som implementerar IAsyncDisposable. - En null-kontroll utförs eftersom
obj
skapas i en livscykelmetod (visas inte).
@implements IAsyncDisposable
...
@code {
...
public async ValueTask DisposeAsync()
{
if (obj is not null)
{
await obj.DisposeAsync();
}
}
}
Mer information finns i:
- ASP.NET Core Blazor synkroniseringskontext
- Rensa ohanterade resurser (.NET-dokumentation)
- Nullvillkorliga operatorer ?. och? []
Tilldelning av null
till kasserade objekt
Vanligtvis behöver du inte tilldela null
till borttagna objekt när du har anropat Dispose/DisposeAsync. Sällsynta fall för att tilldela null
omfattar följande:
- Om objektets typ är dåligt implementerad och inte tolererar upprepade anrop till Dispose/DisposeAsynctilldelar du
null
efter bortskaffande för att smidigt hoppa över ytterligare anrop till Dispose/DisposeAsync. - Om en långlivad process fortsätter att innehålla en referens till ett bortskaffat objekt kan
null
skräpinsamlare frigöra objektet trots att den långvariga processen innehåller en referens till det.
Det här är ovanliga scenarier. För objekt som implementeras korrekt och fungerar normalt är det ingen idé att tilldela null
till borttagna objekt. I de sällsynta fall där ett objekt måste tilldelas null
rekommenderar vi att du dokumenterar orsaken och söker en lösning som förhindrar behovet av att tilldela null
.
StateHasChanged
Anteckning
Det går inte att anropa StateHasChanged i Dispose
och DisposeAsync
.
StateHasChanged kan anropas som en del av att avveckla renderaren, så att begära uppdateringar av användargränssnittet stöds inte vid den tidpunkten.
Händelsehanterare
Avregistrera alltid händelsehanterare från .NET-händelser. Följande Blazor formulär exempel visar hur du avregistrerar en händelsehanterare i metoden Dispose
.
Privat fält och lambda-tillvägagångssätt:
@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;
}
}
Tillvägagångssätt för privata metoder
@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;
}
}
Mer information om EditForm komponent och formulär finns i översikten över ASP.NET Core Blazor-formulär och de andra formulärartiklarna i noden Forms.
Anonyma funktioner, metoder och uttryck
När anonyma funktioner, metoder eller uttryck används är det inte nödvändigt att implementera IDisposable och avprenumerera delegater. Att inte avregistrera en delegat är dock ett problem när objektet som exponerar händelsen överstiger livslängden för komponenten som registrerar delegaten. När detta inträffar uppstår en minnesläcka eftersom det registrerade ombudet håller kvar det ursprungliga objektet. Använd därför endast följande metoder när du vet att händelsedelegaten tar bort snabbt. När du är osäker på livslängden för objekt som kräver bortskaffande, prenumerera på en delegatmetod och avyttra delegaten korrekt, som de tidigare exemplen visar.
Anonym lambda-metod (explicit bortskaffande krävs inte):
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);
}
Metod för anonyma lambda-uttryck (explicit bortskaffande krävs inte):
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);
}
Det fullständiga exemplet på föregående kod med anonyma lambda-uttryck visas i artikeln ASP.NET Core Blazor formulärverifiering.
Mer information finns i Rensa ohanterade resurser och de avsnitt som följer på implementeringen av metoderna Dispose
och DisposeAsync
.
Bortskaffande under JS interoperabilitet
Fånga upp JSDisconnectedException i möjliga fall där förlusten av krets SignalR hos Blazorförhindrar interop-anropen av JS vilket resulterar i ett ohanterat undantag.
Mer information finns i följande resurser:
ASP.NET Core