Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Finalizátory (historicky označované jako destruktory) slouží k provedení jakéhokoli nezbytného konečného vyčištění v okamžiku, kdy je instance třídy shromažďována systémem garbage collector. Ve většině případů se můžete vyhnout zápisu finalizátoru tím, že použijete System.Runtime.InteropServices.SafeHandle nebo odvozené třídy k zabalení všech nespravovaných popisovačů.
Poznámky
- Finalizační metody nelze definovat ve strukturách. Používají se pouze ve třídách.
- Třída může mít pouze jeden finalizátor.
- Finalizery nelze zdědit ani přetížit.
- Nelze volat finalizéry. Úkoly se vyvolávají automaticky.
- Finalizátor nepřebírá modifikátory ani nemá parametry.
Následuje například deklarace finalizátoru Car pro třídu.
class Car
{
~Car() // finalizer
{
// cleanup statements...
}
}
Finalizátor lze také implementovat jako definici těla výrazu, jak ukazuje následující příklad.
public class Destroyer
{
public override string ToString() => GetType().Name;
~Destroyer() => Console.WriteLine($"The {ToString()} finalizer is executing.");
}
Finalizátor implicitně volá Finalize na základní třídě objektu. Proto je volání finalizátoru implicitně přeloženo do následujícího kódu:
protected override void Finalize()
{
try
{
// Cleanup statements...
}
finally
{
base.Finalize();
}
}
Tento návrh znamená, že Finalize metoda je volána rekurzivně pro všechny instance v řetězci dědičnosti, od nejvíce odvozené po nejméně odvozené.
Poznámka:
Prázdné finalizéry by se neměly používat. Pokud třída obsahuje finalizátor, je ve frontě Finalize vytvořena položka. Tato fronta je zpracovávána správcem paměti. Když GC zpracuje frontu, volá každý finalizátor. Zbytečné finalizační metody, včetně prázdných finalizátorů, finalizátorů, které volají pouze finalizátor základní třídy, nebo finalizátory, které volají pouze podmíněně generované metody, způsobují zbytečné ztráty výkonu.
Programátor nemá žádnou kontrolu nad tím, kdy je volán finalizátor; sběrač paměti rozhoduje, kdy ho zavolat. Garbage collector vyhledává objekty, které už aplikace nepoužívá. Pokud se domnívá, že objekt má nárok na dokončení, zavolá finalizátor (pokud existuje) a uvolní paměť použitou k uložení objektu. Je možné vynutit uvolňování paměti voláním Collect, ale ve většině případů by se tomu volání mělo vyhnout, protože může způsobit problémy s výkonem.
Poznámka:
Zda jsou finalizátory spuštěny jako součást ukončení aplikace, je specifické pro každou implementaci rozhraní .NET. Když se aplikace ukončí, rozhraní .NET Framework vynakládá veškeré rozumné úsilí volat finalizační metody pro objekty, které ještě nebyly zpracovány garbage collectorem, pokud takové vyčištění nebylo potlačeno (například voláním metody GC.SuppressFinalize knihovny). .NET 5 (včetně .NET Core) a novějších verzí nevolají finalizátory jako součást ukončení aplikace. Další informace najdete v tématu o problému GitHubu dotnet/csharpstandard #291.
Pokud potřebujete spolehlivě vyčistit při ukončení aplikace, zaregistrujte obslužnou rutinu pro událost System.AppDomain.ProcessExit. Tato obslužná rutina zajistí, že IDisposable.Dispose() (nebo IAsyncDisposable.DisposeAsync()) byla volána pro všechny objekty, které vyžadují vyčištění před ukončením aplikace. Protože nemůžete volat Finalize přímo a nemůžete zaručit, že garbage collector volá všechny finalizační metody před ukončením, musíte použít Dispose nebo DisposeAsync, abyste zajistili uvolnění prostředků.
Použití finalizátorů k uvolnění prostředků
Obecně platí, že jazyk C# nevyžaduje tolik správy paměti od vývojáře jako jazyky, které nevyužívají modul runtime s automatickou správou paměti. Důvodem je to, že garbage collector v .NETu implicitně spravuje přidělení a uvolnění paměti pro vaše objekty. Pokud však vaše aplikace zapouzdřuje nespravované prostředky, jako jsou okna, soubory a síťová připojení, měli byste tyto prostředky uvolnit pomocí finalizačních metod. Pokud je objekt způsobilý k finalizaci, garbage collector spustí Finalize metodu objektu.
Výstraha
Nepřistupujte ke členům spravovaného objektu z finalizátoru. Během finalizace mohou být spravované objekty již zrušené, čímž se stávají nedostupnými nebo v neplatném stavu. Přístup pouze k nespravovaným prostředkům přímo z finalizátorů.
Explicitní vydání prostředků
Pokud vaše aplikace používá nákladný externí prostředek, doporučujeme také poskytnout způsob, jak prostředek explicitně uvolnit dříve, než objekt uvolní program pro automatické uvolňování paměti. Chcete-li uvolnit prostředek, implementujte metodu Dispose z rozhraní IDisposable, který provádí potřebné vyčištění objektu. To může výrazně zlepšit výkon aplikace. I při této explicitní kontrole nad prostředky působí finalizátor jako pojistka pro vyčištění prostředků, pokud volání Dispose metody selže.
Další informace o čištění prostředků najdete v následujících článcích:
- Čištění nespravovaných prostředků
- Implementace metody Dispose
- Implementace metody DisposeAsync
-
usingvýrok
Příklad
Následující příklad vytvoří tři třídy, které tvoří řetězec dědičnosti. Třída First je základní třída, Second je odvozena z Firsta Third je odvozena z Second. Všechny tři mají finalizátory. V Main je vytvořena instance nejvíce odvozené třídy. Výstup z tohoto kódu závisí na implementaci rozhraní .NET, na kterou aplikace cílí.
- .NET Framework: Výstup ukazuje, že finalizační metody pro tři třídy jsou volány automaticky při ukončení aplikace v pořadí od nejvíce odvozeného po nejméně odvozené.
- .NET 5 (včetně .NET Core) nebo novější verze: Neexistuje žádný výstup, protože tato implementace .NET při ukončení aplikace nevolá finalizátory.
class First
{
~First()
{
System.Diagnostics.Trace.WriteLine("First's finalizer is called.");
}
}
class Second : First
{
~Second()
{
System.Diagnostics.Trace.WriteLine("Second's finalizer is called.");
}
}
class Third : Second
{
~Third()
{
System.Diagnostics.Trace.WriteLine("Third's finalizer is called.");
}
}
/*
Test with code like the following:
Third t = new Third();
t = null;
When objects are finalized, the output would be:
Third's finalizer is called.
Second's finalizer is called.
First's finalizer is called.
*/
specifikace jazyka C#
Další informace naleznete v části Finalizers specifikace jazyka C#.