Finalizační metody (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í při shromažďování instance třídy 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 nebo odvozených tříd zabalit všechny nespravované popisovače.

Poznámky

  • Finalizační metody nelze definovat ve strukturách. Používají se pouze s třídami.
  • Třída může mít pouze jeden finalizátor.
  • Finalizační metody nelze zdědit ani přetížit.
  • Finalizační metody nelze volat. Volají se automaticky.
  • Finalizátor nepřebírají modifikátory ani nemají 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 základní třídu 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é finalizační metody by neměly být použity. Pokud třída obsahuje finalizátor, položka je vytvořena ve frontě Finalize . Tato fronta je zpracována uvolňováním paměti. Když GC zpracuje frontu, volá každou finalizační metodu. 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ána finalizátor; systém uvolňování paměti se rozhodne, kdy se má volat. Systém uvolňování paměti kontroluje 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 toto 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 se snaží volat finalizační metody pro objekty, které ještě nebyly uvolněny z paměti, pokud takové vyčištění nebylo potlačeno (například voláním metody GC.SuppressFinalizeknihovny). .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 při ukončení aplikace spolehlivě vyčistit, zaregistrujte obslužnou rutinu System.AppDomain.ProcessExit události. Tato obslužná rutina zajistí IDisposable.Dispose() , IAsyncDisposable.DisposeAsync()že 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 systém uvolňování paměti volá všechny finalizační metody 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 pro část 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 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 dokončení, systém uvolňování paměti spustí Finalize metodu objektu.

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 předtím, než uvolňování paměti objekt uvolní. Chcete-li uvolnit prostředek, implementujte metodu DisposeIDisposable z rozhraní, které provádí nezbytné vyčištění objektu. To může výrazně zlepšit výkon aplikace. I když je tato explicitní kontrola nad prostředky, finalizátor se stane zárukou 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:

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 Maininstanci nejvíce odvozené třídy je vytvořena. Výstup z tohoto kódu závisí na tom, jakou implementaci rozhraní .NET cílí aplikace:

  • .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#.

Viz také