Share via


Finalizers (C# programozási útmutató)

A véglegesítők (korábbi nevén destruktorok) a szükséges végleges tisztítás végrehajtására szolgálnak, amikor a szemétgyűjtő összegyűjt egy osztálypéldányt. A legtöbb esetben elkerülheti a véglegesítő írását, ha a nem felügyelt leírók burkolására használja az System.Runtime.InteropServices.SafeHandle vagy származtatott osztályokat.

Megjegyzések

  • A véglegesítők nem definiálhatók a szerkezetekben. Csak osztályokkal használják őket.
  • Egy osztálynak csak egy döntőse lehet.
  • A véglegesítők nem örökölhetők vagy túlterhelhetők.
  • A döntőzők nem hívhatók meg. A rendszer automatikusan meghívja őket.
  • A véglegesítők nem fogadnak módosítókat, és nem rendelkeznek paraméterekkel.

Az alábbiakban például az osztály döntősének deklarációja látható Car .

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

A véglegesítő kifejezés törzsdefinícióként is implementálható, ahogy az alábbi példa is mutatja.

public class Destroyer
{
   public override string ToString() => GetType().Name;

   ~Destroyer() => Console.WriteLine($"The {ToString()} finalizer is executing.");
}

A véglegesítő implicit módon meghívja Finalize az objektum alaposztályát. Ezért a véglegesítő hívás implicit módon a következő kódra lesz lefordítva:

protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}

Ez a kialakítás azt jelenti, hogy a Finalize metódust rekurzívan nevezik az öröklési lánc összes példányára, a legtöbb származtatotttól a legkevésbé származtatottig.

Feljegyzés

Üres véglegesítőket nem szabad használni. Ha egy osztály véglegesítőt tartalmaz, a rendszer létrehoz egy bejegyzést az Finalize üzenetsorban. Ezt az üzenetsort a szemétgyűjtő dolgozza fel. Amikor a GC feldolgozza az üzenetsort, meghívja az egyes véglegesítőket. A szükségtelen véglegesítők, beleértve az üres döntősöket, a csak az alaposztály-döntőzőt meghívó döntősöket, vagy a csak feltételesen kibocsátott metódusokat hívó döntősök szükségtelen teljesítménycsökkenést okoznak.

A programozó nem szabályozhatja a döntőző meghívását; a szemétgyűjtő dönti el, hogy mikor hívja meg. A szemétgyűjtő ellenőrzi azokat az objektumokat, amelyeket az alkalmazás már nem használ. Ha úgy véli, hogy egy objektum jogosult a véglegesítésre, meghívja a véglegesítőt (ha van ilyen), és visszanyeri az objektum tárolásához használt memóriát. A hívással Collectkényszeríthető a szemétgyűjtés, de a legtöbb esetben ezt a hívást el kell kerülni, mert teljesítményproblémákat okozhat.

Feljegyzés

A .NET minden implementációjára jellemző, hogy a véglegesítők az alkalmazásleállítás részeként futnak-e. Amikor egy alkalmazás leáll, .NET-keretrendszer minden ésszerű erőfeszítést megtesz annak érdekében, hogy véglegesítőket hívjon meg olyan objektumokhoz, amelyeket még nem gyűjtöttek össze, kivéve, ha az ilyen törlést letiltották (például a kódtár metódusának GC.SuppressFinalizehívásával). A .NET 5 (beleértve a .NET Core-t) és a későbbi verziók nem hívják meg a véglegesítőket az alkalmazás leállítása részeként. További információ: GitHub-probléma dotnet/csharpstandard #291.

Ha megbízhatóan kell elvégeznie a tisztítást, amikor egy alkalmazás kilép, regisztráljon egy kezelőt az System.AppDomain.ProcessExit eseményhez. Ez a kezelő biztosítja IDisposable.Dispose() (vagy) az összes olyan objektum meghívását, IAsyncDisposable.DisposeAsync()amely törlést igényel az alkalmazás kilépése előtt. Mivel nem hívhatja meg közvetlenül a Finalize-t, és nem garantálhatja, hogy a szemétgyűjtő minden véglegesítőt meghív a kilépés előtt, az erőforrások felszabadításához vagy DisposeAsync használatához kell használniaDispose.

Véglegesítők használata erőforrások kiadásához

A C# általában nem igényel annyi memóriakezelést a fejlesztő részéről, mint a szemétgyűjtéssel nem rendelkező futtatókörnyezetet megcélozó nyelvek. Ennek az az oka, hogy a .NET szemétgyűjtő implicit módon kezeli az objektumok memóriafoglalását és felszabadítását. Ha azonban az alkalmazás nem felügyelt erőforrásokat, például windowsokat, fájlokat és hálózati kapcsolatokat foglal magában, a véglegesítőkkel szabadíthatja fel ezeket az erőforrásokat. Ha az objektum jogosult a véglegesítésre, a szemétgyűjtő futtatja az Finalize objektum metódusát.

Erőforrások explicit kiadása

Ha az alkalmazás költséges külső erőforrást használ, azt is javasoljuk, hogy adjon módot az erőforrás explicit kiadására, mielőtt a szemétgyűjtő felszabadítja az objektumot. Az erőforrás felszabadításához implementáljon egy metódust Dispose a IDisposable felületről, amely elvégzi az objektumhoz szükséges tisztítást. Ez jelentősen javíthatja az alkalmazás teljesítményét. Az erőforrások explicit vezérlése mellett is a véglegesítő védelmet nyújt az erőforrások megtisztításához, ha a Dispose metódus meghívása sikertelen.

Az erőforrások megtisztításáról az alábbi cikkekben talál további információt:

Példa

Az alábbi példa három osztályt hoz létre, amelyek öröklési láncot alkotnak. Az osztály First az alaposztály, Second a származtatása Firstés Third a származtatása Second. Mindhárman döntősök. Ebben Maina fájlban létrejön a legtöbb származtatott osztály egy példánya. A kód kimenete attól függ, hogy az alkalmazás melyik .NET-implementációt célozza meg:

  • .NET-keretrendszer: A kimenet azt mutatja, hogy a három osztály véglegesítőit a rendszer automatikusan meghívja az alkalmazás leállásakor, a legtöbb származtatotttól a legkevésbé származtatottig.
  • .NET 5 (beleértve a .NET Core-t) vagy egy újabb verzió: Nincs kimenet, mert a .NET ezen implementációja nem hív meg véglegesítőket az alkalmazás leállásakor.
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.
*/

C# nyelvspecifikáció

További információkért lásd a C#-nyelv specifikációjának Finalizers szakaszát.

Lásd még