Ártalmatlanítási módszer implementálása

A Dispose metódus elsősorban a nem felügyelt erőforrások kiadására van implementálva. Amikor implementációkként működő IDisposable példánytagokkal dolgozik, gyakori a kaszkádolt Dispose hívások használata. Más okokból is megvalósítható például a lefoglalt memória felszabadítása Dispose, egy gyűjteményhez hozzáadott elem eltávolítása vagy a beszerzett zárolás kiadásának jelzése.

A .NET-szemétgyűjtő nem foglal le és nem szabadít fel nem felügyelt memóriát. Az objektum elidegenítésének mintája, más néven az elidegenítési minta, rendet szab egy objektum élettartamára. Az elidegenítési minta az interfészt megvalósító IDisposable objektumokhoz használatos. Ez a minta gyakori a fájl- és csőfogópontok, a beállításjegyzék fogópontjai, a várakozási fogópontok vagy a nem felügyelt memóriablokkokra mutató mutatók használatakor, mivel a szemétgyűjtő nem tudja visszavenni a nem felügyelt objektumokat.

Annak érdekében, hogy az erőforrások mindig megfelelően legyenek megtisztítva, egy Dispose metódusnak idempotensnek kell lennie, hogy többször is hívható legyen kivétel nélkül. Ezenkívül a további meghívások Dispose nem tehetnek semmit.

A metódushoz megadott kód példája bemutatja, hogy a GC.KeepAlive szemétgyűjtés hogyan okozhatja a véglegesítő futtatását, miközben az objektumra vagy annak tagjaira mutató nem felügyelt hivatkozás még használatban van. Érdemes lehet arra használni GC.KeepAlive , hogy az objektum nem jogosult a szemétgyűjtésre az aktuális rutin kezdetétől a metódus meghívásának pontig.

Tipp.

A függőséginjektálás tekintetében a szolgáltatások IServiceCollectionregisztrálásakor a szolgáltatás élettartama implicit módon történik az Ön nevében. Az IServiceProvider és a hozzá tartozó IHost vezénylési erőforrás-törlés. Pontosabban az implementációk IDisposableIAsyncDisposable és azok megfelelően vannak megsemmisítve a megadott élettartamuk végén.

További információ: Függőséginjektálás a .NET-ben.

Széf fogópontok

Az objektum véglegesítőjének kódjának írása összetett feladat, amely problémákat okozhat, ha nem megfelelően történik. Ezért azt javasoljuk, hogy a véglegesítő helyett objektumokat hozzon létre System.Runtime.InteropServices.SafeHandle .

Az A System.Runtime.InteropServices.SafeHandle egy absztrakt felügyelt típus, amely egy nem felügyelt erőforrást azonosító burkoló típust burkol System.IntPtr . Windows rendszeren azonosíthat egy leírót, a Unixban pedig egy fájlleírót. Ez SafeHandle biztosítja az összes olyan logikát, amely annak biztosításához szükséges, hogy az erőforrás egyszer és csak egyszer legyen felszabadítva, akár a SafeHandle törléskor, akár az összes, a SafeHandle példányra mutató hivatkozás elvetésekor, és a SafeHandle példány véglegesítésekor.

Ez System.Runtime.InteropServices.SafeHandle egy absztrakt alaposztály. A származtatott osztályok meghatározott példányokat biztosítanak a különböző típusú leírókhoz. Ezek a származtatott osztályok ellenőrzik, hogy a System.IntPtr rendszer mely értékeket tekinti érvénytelennek, és hogyan szabadíthatja fel ténylegesen a leírót. Például SafeFileHandle a nyitott fájlleírókat/leírókat azonosító burkolásból SafeHandleIntPtrs származik, és felülbírálja annak bezárására szolgáló metódusát SafeHandle.ReleaseHandle() (a Unix-függvényen vagy CloseHandle a close Windows-függvényen keresztül). A nem felügyelt erőforrást létrehozó .NET-kódtárakban található API-k többsége a nyers mutató visszaadása helyett szükség szerint becsomagolja SafeHandle azt, és visszaadja SafeHandle Önnek. Olyan helyzetekben, amikor nem felügyelt összetevővel kommunikál, és nem felügyelt erőforrást kap IntPtr , létrehozhat egy saját SafeHandle típust a burkoláshoz. Ennek eredményeképpen kevés nem típusnakSafeHandle kell véglegesítőket implementálnia. A legtöbb eldobható minta implementáció csak más felügyelt erőforrások burkolását végzi, amelyek közül néhány objektum lehet SafeHandle .

A névtér következő Microsoft.Win32.SafeHandles származtatott osztályai biztonságos leírókat biztosítanak.

Osztály Az általa birtokolt erőforrások
SafeFileHandle
SafeMemoryMappedFileHandle
SafePipeHandle
Fájlok, memórialeképezett fájlok és csövek
SafeMemoryMappedViewHandle Memórianézetek
SafeNCryptKeyHandle
SafeNCryptProviderHandle
SafeNCryptSecretHandle
Titkosítási szerkezetek
SafeRegistryHandle Beállításkulcs
SafeWaitHandle Várakozási fogópontok

Megsemmisítés() és megsemmisítés(bool)

Az IDisposable interfészhez egyetlen paraméter nélküli metódus implementálása szükséges. Dispose Emellett minden nem lezárt osztálynak túlterhelési Dispose(bool) módszerrel kell rendelkeznie.

A metódus-aláírások a következők:

  • public nem virtuális (NotOverridable a Visual Basicben) (IDisposable.Dispose implementáció).
  • protected virtual (Overridable a Visual Basicben) Dispose(bool).

A Dispose() metódus

Mivel a publicnem virtuális (NotOverridable Visual Basicben) paraméter nélküli Dispose metódust akkor hívunk meg, amikor már nincs rá szükség (egy ilyen típusú felhasználó), a célja, hogy felszabadítsa a nem felügyelt erőforrásokat, általános tisztítást végezzen, és jelezze, hogy a véglegesítőnek , ha van ilyen, nem kell futtatnia. A felügyelt objektumhoz társított tényleges memória felszabadítása mindig a szemétgyűjtő tartománya. Emiatt szabványos implementációval rendelkezik:

public void Dispose()
{
    // Dispose of unmanaged resources.
    Dispose(true);
    // Suppress finalization.
    GC.SuppressFinalize(this);
}
Public Sub Dispose() _
    Implements IDisposable.Dispose
    ' Dispose of unmanaged resources.
    Dispose(True)
    ' Suppress finalization.
    GC.SuppressFinalize(Me)
End Sub

A Dispose metódus elvégzi az összes objektumkarbantartást, így a szemétgyűjtőnek már nem kell meghívnia az objektumok felülbírálását Object.Finalize . Ezért a SuppressFinalize metódus meghívása megakadályozza, hogy a szemétgyűjtő futtassa a véglegesítőt. Ha a típusnak nincs véglegesítője, a hívásnak GC.SuppressFinalize nincs hatása. A tényleges tisztítást a metódus túlterhelése Dispose(bool) hajtja végre.

Az Dispose(bool) metódus túlterhelése

A túlterhelésben a disposing paraméter azt Boolean jelzi, hogy a metódushívás egy Dispose metódusból (az értéke true) vagy egy véglegesítőből (az falseértéke) származik-e.

protected virtual void Dispose(bool disposing)
{
    if (_disposed)
    {
        return;
    }

    if (disposing)
    {
        // TODO: dispose managed state (managed objects).
    }

    // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
    // TODO: set large fields to null.

    _disposed = true;
}
Protected Overridable Sub Dispose(disposing As Boolean)
     If disposed Then Exit Sub	

     ' A block that frees unmanaged resources.
     
     If disposing Then
         ' Deterministic call…
         ' A conditional block that frees managed resources.    	
     End If
     
     disposed = True
End Sub

Fontos

A disposing paraméternek akkor kell lennie false , ha egy döntőstől hívjuk meg, és true amikor a IDisposable.Dispose metódusból hívjuk meg. Más szóval, akkor, true amikor determinisztikusan hívják, és false amikor nem determinisztikusan hívják.

A metódus törzse három kódblokkból áll:

  • A feltételes visszatérés blokkja, ha az objektum már el van dobva.

  • Nem felügyelt erőforrásokat felszabadító blokk. Ez a blokk a paraméter értékétől disposing függetlenül fut.

  • Egy feltételes blokk, amely felszabadítja a felügyelt erőforrásokat. Ez a blokk akkor fut, ha az értéke disposing .true Az általa felszabadított felügyelt erőforrások a következők lehetnek:

    • Felügyelt objektumok, amelyek implementálják a .IDisposable A feltételes blokk használható a végrehajtás meghívására Dispose (kaszkádolt megsemmisítés). Ha származtatott osztályt System.Runtime.InteropServices.SafeHandle használt a nem felügyelt erőforrás burkolásához, itt kell meghívnia az implementációt SafeHandle.Dispose() .

    • Olyan felügyelt objektumok, amelyek nagy mennyiségű memóriát használnak fel, vagy kevés erőforrást használnak fel. Rendeljen nagy méretű felügyelt objektumhivatkozásokat null , hogy nagyobb valószínűséggel legyenek elérhetetlenek. Ez gyorsabban bocsátja ki őket, mintha nemdeterminisztikus módon lettek volna visszanyerve.

Ha a metódushívás véglegesítőből származik, csak a nem felügyelt erőforrásokat felszabadító kódot kell végrehajtani. A implementátor feladata annak biztosítása, hogy a hamis elérési út ne működjön együtt az esetleg elvetett felügyelt objektumokkal. Ez azért fontos, mert az a sorrend, amelyben a szemétgyűjtő a véglegesítés során eltávolítja a felügyelt objektumokat, nem határozható meg.

Kaszkádolt elidegenítési hívások

Ha az osztály egy mező vagy tulajdonság tulajdonosa, és a típusa implementálódik IDisposable, akkor magát az osztályt is implementálnia IDisposablekell. A példányokat példányosító IDisposable és példánytagként tárolt osztály is felelős a karbantartásért. Ez segít biztosítani, hogy a hivatkozott eldobható típusok lehetőséget kapjanak a tisztítás determinisztikus végrehajtására a Dispose módszeren keresztül. Az alábbi példában az osztály (vagy NotInheritable a Visual Basicben) található sealed .

using System;

public sealed class Foo : IDisposable
{
    private readonly IDisposable _bar;

    public Foo()
    {
        _bar = new Bar();
    }

    public void Dispose() => _bar.Dispose();
}
Public NotInheritable Class Foo
    Implements IDisposable

    Private ReadOnly _bar As IDisposable

    Public Sub New()
        _bar = New Bar()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        _bar.Dispose()
    End Sub
End Class

Tipp.

  • Ha az osztály rendelkezik mezővel IDisposable vagy tulajdonságmal, de nem rendelkezik vele , vagyis az osztály nem hozza létre az objektumot, akkor az osztálynak nem kell implementálnia IDisposable.
  • Előfordulhat, hogy a véglegesítőben szeretne -ellenőrzést végezni null(amely magában foglalja a Dispose(false) véglegesítő által meghívott metódust). Az egyik elsődleges ok az, ha nem biztos abban, hogy a példány teljesen inicializálva lett-e (például kivételt jelenthet egy konstruktor).

Az elidegenítési minta implementálása

Minden nem lezárt osztályt (vagy a nem módosított NotInheritableVisual Basic osztályt) potenciális alaposztálynak kell tekinteni, mert örökölhetők. Ha implementálja az elidegenítési mintát bármely lehetséges alaposztályhoz, a következőket kell megadnia:

  • A Dispose metódust meghívó Dispose(bool) implementáció.
  • A Dispose(bool) tényleges törlést végrehajtó módszer.
  • SafeHandle A nem felügyelt erőforrást burkoló osztály (ajánlott) vagy a Object.Finalize metódus felülbírálása. Az SafeHandle osztály egy döntőzőt biztosít, így nem kell saját maga írnia egyet.

Fontos

Egy alaposztály csak felügyelt objektumokra hivatkozhat, és implementálhatja az elidegenítési mintát. Ezekben az esetekben a véglegesítő szükségtelen. Véglegesítőre csak akkor van szükség, ha közvetlenül nem felügyelt erőforrásokra hivatkozik.

Íme egy általános példa a biztonságos leírót használó alaposztály elidegenítési mintájának implementálására.

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

public class BaseClassWithSafeHandle : IDisposable
{
    // To detect redundant calls
    private bool _disposedValue;

    // Instantiate a SafeHandle instance.
    private SafeHandle? _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                _safeHandle?.Dispose();
                _safeHandle = null;
            }

            _disposedValue = true;
        }
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Public Class BaseClassWithSafeHandle
    Implements IDisposable

    ' To detect redundant calls
    Private _disposedValue As Boolean

    ' Instantiate a SafeHandle instance.
    Private _safeHandle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    ' Public implementation of Dispose pattern callable by consumers.
    Public Sub Dispose() _
               Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not _disposedValue Then

            If disposing Then
                _safeHandle?.Dispose()
                _safeHandle = Nothing
            End If

            _disposedValue = True
        End If
    End Sub
End Class

Feljegyzés

Az előző példa egy objektumot SafeFileHandle használ a minta szemléltetésére; ehelyett bármely származtatott SafeHandle objektum használható. Vegye figyelembe, hogy a példa nem hozza létre megfelelően az objektumot SafeFileHandle .

Az alábbi általános minta egy felülbíráló Object.Finalizealaposztály elidegenítési mintájának implementálására használható.

using System;

public class BaseClassWithFinalizer : IDisposable
{
    // To detect redundant calls
    private bool _disposedValue;

    ~BaseClassWithFinalizer() => Dispose(false);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects)
            }

            // TODO: free unmanaged resources (unmanaged objects) and override finalizer
            // TODO: set large fields to null
            _disposedValue = true;
        }
    }
}
Public Class BaseClassWithFinalizer
    Implements IDisposable

    ' To detect redundant calls
    Private _disposedValue As Boolean

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub

    ' Public implementation of Dispose pattern callable by consumers.
    Public Sub Dispose() _
               Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not _disposedValue Then

            If disposing Then
                ' TODO: dispose managed state (managed objects)
            End If

            ' TODO free unmanaged resources (unmanaged objects) And override finalizer
            ' TODO: set large fields to null
            _disposedValue = True
        End If
    End Sub
End Class

Tipp.

A C#-ban egy véglegesítést úgy valósít meg, hogy egy véglegesítőt ad meg, nem pedig felül kell bírálniObject.Finalize. A Visual Basicben létrehoz egy véglegesítőt a következővel Protected Overrides Sub Finalize(): .

A származtatott osztály elidegenítési mintájának implementálása

Az interfészt megvalósító IDisposable osztályból származó osztály nem implementálható IDisposable, mert az alaposztály implementálását IDisposable.Dispose a származtatott osztályok öröklik. Ehelyett egy származtatott osztály törléséhez a következőket kell megadnia:

  • Olyan protected override void Dispose(bool) metódus, amely felülbírálja az alaposztály metódusát, és elvégzi a származtatott osztály tényleges törlését. Ennek a metódusnak argumentumként kell meghívnia a base.Dispose(bool) (MyBase.Dispose(bool) Visual Basic-ben) metódust is, amely átadja neki az állapotot (bool disposing paramétert).
  • SafeHandle A nem felügyelt erőforrást burkoló osztály (ajánlott) vagy a Object.Finalize metódus felülbírálása. Az SafeHandle osztály egy véglegesítőt biztosít, amely megszabadít attól, hogy egy kódot kelljen kódhoz adnia. Ha véglegesítőt ad meg, a Dispose(bool) túlterhelést argumentummal false kell meghívnia.

Íme egy példa a biztonságos leírót használó származtatott osztály elidegenítési mintájának implementálására szolgáló általános mintára:

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

public class DerivedClassWithSafeHandle : BaseClassWithSafeHandle
{
    // To detect redundant calls
    private bool _disposedValue;

    // Instantiate a SafeHandle instance.
    private SafeHandle? _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                _safeHandle?.Dispose();
                _safeHandle = null;
            }

            _disposedValue = true;
        }

        // Call base class implementation.
        base.Dispose(disposing);
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Public Class DerivedClassWithSafeHandle
    Inherits BaseClassWithSafeHandle

    ' To detect redundant calls
    Private _disposedValue As Boolean

    ' Instantiate a SafeHandle instance.
    Private _safeHandle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If Not _disposedValue Then

            If disposing Then
                _safeHandle?.Dispose()
                _safeHandle = Nothing
            End If

            _disposedValue = True
        End If

        ' Call base class implementation.
        MyBase.Dispose(disposing)
    End Sub
End Class

Feljegyzés

Az előző példa egy objektumot SafeFileHandle használ a minta szemléltetésére; ehelyett bármely származtatott SafeHandle objektum használható. Vegye figyelembe, hogy a példa nem hozza létre megfelelően az objektumot SafeFileHandle .

Az alábbi általános minta a felülbíráló Object.Finalizeszármaztatott osztály ártalmatlanítási mintájának implementálására használható:

public class DerivedClassWithFinalizer : BaseClassWithFinalizer
{
    // To detect redundant calls
    private bool _disposedValue;

    ~DerivedClassWithFinalizer() => Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects).
            }

            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.
            _disposedValue = true;
        }

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
Public Class DerivedClassWithFinalizer
    Inherits BaseClassWithFinalizer

    ' To detect redundant calls
    Private _disposedValue As Boolean

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If Not _disposedValue Then

            If disposing Then
                ' TODO: dispose managed state (managed objects).
            End If

            ' TODO free unmanaged resources (unmanaged objects) And override a finalizer below.
            ' TODO: set large fields to null.
            _disposedValue = True
        End If

        ' Call the base class implementation.
        MyBase.Dispose(disposing)
    End Sub
End Class

Lásd még