Finalizátory (Průvodce programováním v C#)

Finalizátory (historicky označované jako destruktory) slouží k provedení jakéhokoli nezbytného konečného vyčištění, když je instance třídy shromažďována uvolňováním paměti. Ve většině případů se můžete vyhnout zápisu finalizátoru pomocí System.Runtime.InteropServices.SafeHandle tříd nebo odvozených tříd k zabalení jakéhokoli nespravovaného úchytu.

Poznámky

  • Finalizátory nelze definovat ve strukturách. Používají se pouze u tříd.
  • Třída může mít pouze jeden finalizátor.
  • Finalizátory nelze dědit ani přetížit.
  • Finalizátory nelze volat. Automaticky se vyvolá.
  • Finalizátor nepřebírají modifikátory ani nemají parametry.

Například následující je deklarace finalizátoru Car třídy.

class Car
{
    ~Car()  // finalizer
    {
        // cleanup statements...
    }
}

Finalizátor lze také implementovat jako definici textu 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 základní třídu objektu. Proto je volání finalizátoru implicitně přeloženo na následující kód:

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átory by se neměly používat. Když třída obsahuje finalizátor, položka se vytvoří ve frontě Finalize . Tato fronta je zpracována uvolňováním paměti. Když GC zpracuje frontu, volá každou finalizátor. Nepotřebné finalizátory, 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 se volá finalizátor; systém uvolňování paměti se rozhodne, kdy ho má volat. Systém uvolňování paměti kontroluje objekty, které už aplikace nepoužívá. Pokud považuje objekt způsobilý k 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 mělo tomuto volání vyhnout, protože může způsobit problémy s výkonem.

Poznámka

Jestli se finalizátory spouští jako součást ukončení aplikace, jsou specifické pro každou implementaci .NET. Když se aplikace ukončí, rozhraní .NET Framework provádí veškeré přiměřené úsilí při volání finalizátorů pro objekty, které ještě nebyly shromažďovány uvolňování paměti, pokud není takové vyčištění potlačeno (voláním metody GC.SuppressFinalizeknihovny , například). .NET 5 (včetně .NET Core) a novějších verzí nevyvolávejte finalizátory jako součást ukončení aplikace. Další informace najdete v tématu GitHub problém s dotnet/csharpstandard #291.

Pokud potřebujete provést spolehlivé vyčištění při ukončení aplikace, zaregistrujte obslužnou rutinu System.AppDomain.ProcessExit události. Tato obslužná rutina by se zajistila IDisposable.Dispose()IAsyncDisposable.DisposeAsync()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 systém uvolňování paměti volá všechny finalizátory před ukončením, musíte použít Dispose nebo DisposeAsync zajistit 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 na straně vývojáře jako jazyky, které necílily modul runtime s uvolňováním paměti. Důvodem je to, že uvolňování paměti .NET implicitně spravuje přidělení a uvolnění paměti pro vaše objekty. Pokud ale vaše aplikace zapouzdří nespravované prostředky, jako jsou okna, soubory a síťová připojení, měli byste tyto prostředky uvolnit pomocí finalizátorů. Pokud je objekt způsobilý k dokončení, systém uvolňování paměti spustí Finalize metodu objektu.

Explicitní verze prostředků

Pokud vaše aplikace používá nákladný externí prostředek, doporučujeme také poskytnout způsob, jak prostředek explicitně uvolnit, než uvolňování paměti objekt uvolní. Pokud chcete prostředek uvolnit, implementujte Dispose metodu IDisposable z rozhraní, které provádí potřebné vyčištění objektu. To může výrazně zlepšit výkon aplikace. I s touto explicitní kontrolou prostředků se finalizátor stane zárukou pro vyčištění prostředků, pokud volání Dispose metody selže.

Další informace o vyčištění prostředků najdete v následujících článcích:

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 od Firsta Third je odvozena z Second. Všichni tři mají finalizátory. V Maininstanci nejvíce odvozené třídy je vytvořena. Výstup z tohoto kódu závisí na tom, jakou implementaci .NET cílí aplikace:

  • .NET Framework: Výstup ukazuje, že finalizátory pro tři třídy se při ukončení aplikace automaticky volají, aby od nejvíce odvozených po nejmenší 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 najdete v části Finalizers specifikace jazyka C#.

Viz také