Megosztás a következőn keresztül:


Á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 IDisposable implementációval rendelkező példánytagokkal dolgozunk, gyakori, hogy egymásra épülő Dispose hívásokat végzünk. A Disposemegvalósításának más okai is vannak, például a lefoglalt memória felszabadítása, a gyűjteményhez hozzáadott elemek 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. A megsemmisítési mintának nevezettobjektum eltávolításának mintája az objektum élettartamára vonatkozó sorrendet szabja meg. A felszabadítási minta a IDisposable interfészt implementáló objektumokhoz használatos. Ez a minta gyakori a fájl- és csőkezelők, a beállításjegyzék kezelői, a várakozási kezelők vagy a kezeletlen memória blokkjaira mutató mutatók használatakor, mivel a szemétgyűjtő nem tudja visszavenni a kezeletlen objektumokat.

Annak érdekében, hogy az erőforrások mindig megfelelően legyenek felszabadítva, egy Dispose metódusnak idempotensnek kell lennie, így többször is hívható, anélkül hogy kivételt okozna. Ezenkívül a Dispose későbbi meghívásainak nem kell semmit tennie.

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

Borravaló

Függőséginjektálás esetén, amikor szolgáltatásokat regisztrálunk egy IServiceCollection-ben, a szolgáltatás élettartamának kezelése automatikusan történik az Ön helyett. A IServiceProvider és a hozzá tartozó IHost az erőforrás-törlést vezényli. A IDisposable és a IAsyncDisposable implementációi a megadott élettartamuk végén vannak megfelelően megsemmisítve.

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

Kaszkádolt felszabadítási hívások

Ha az osztály rendelkezik egy IDisposable-t implementáló másik típus példányával, akkor az osztálynak magának is implementálnia kell a IDisposable-et. Általában egy osztály, amely példányosít egy IDisposable-implementációt, és példánytagként (vagy tulajdonságként) tárolja, szintén felelős a törlésért. Ez segít biztosítani, hogy a hivatkozott eldobható típusoknak lehetőségük legyen determinált módon tisztítást végezni a Dispose módszerrel. Az alábbi példában az osztály sealed (vagy a Visual Basicben NotInheritable).

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

Borravaló

  • Ha az osztálynak van egy IDisposable mezője vagy tulajdonsága, de nem saját, akkor az osztálynak nem kell implementálnia IDisposable. A IDisposable gyermekobjektumot létrehozó és tároló osztály általában szintén tulajdonos lesz, de bizonyos esetekben a tulajdonjog átkerülhet egy másik IDisposable típusba.
  • Vannak olyan esetek, amikor null-ellenőrzést szeretne végezni egy véglegesítőben (ami magában foglalja a véglegesítő által hívott Dispose(false) metódust). Az egyik elsődleges ok az, ha nem biztos abban, hogy a példány teljesen inicializálva van-e (például kivétel dobódhat egy konstruktorban).

Dispose() és Dispose(bool)

A 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 Dispose(bool) túlterhelési módszerrel kell rendelkeznie.

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

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

A Dispose() metódus

Mivel a public, nem virtuális (NotOverridable a Visual Basicben) paraméter nélküli Dispose metódust hívunk meg, amikor már nincs rá szükség (a típus felhasználója), a célja a nem felügyelt erőforrások felszabadítása, az általános tisztítás végrehajtása, valamint annak jelzése, 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ási feladatot, így a szemétgyűjtőnek többé nincs szüksége arra, hogy meghívja az objektumok Object.Finalize felülbírálását. Ezért a SuppressFinalize metódus hí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 GC.SuppressFinalize hívásának nincs hatása. Az aktuális tisztítást a Dispose(bool) metódus (overload) hajtja végre.

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

A túlterhelésnél a disposing paraméter egy Boolean, amely azt jelzi, hogy a metódushívás egy Dispose metódusból (értéke true) vagy egy döntőstől származik (értéke false).

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

    if (disposing)
    {
        // Dispose managed state (managed objects).
        // ...
    }

    // Free unmanaged resources.
    // ...

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

     If disposing Then

         ' Free managed resources.
         ' ...

     End If

     ' Free unmanaged resources.
     ' ...

     disposed = True
End Sub

Fontos

A disposing paraméternek false kell lennie, amikor egy finalizálóból hívják meg, és true a IDisposable.Dispose metódusból való meghíváskor. Más szóval, akkor true, ha determinisztikusan hívják, és false, ha nemdeterminisztikusan 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 lebontásra került.

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

    • Felügyelt objektumok, amelyek IDisposableimplementálnak. A feltételes blokk a Dispose implementáció meghívására használható (kaszkádolt megsemmisítés). Ha egy System.Runtime.InteropServices.SafeHandle származtatott osztályt használt a nem felügyelt erőforrás burkolására, itt meg kell hívnia a SafeHandle.Dispose() implementációt.
    • Olyan felügyelt objektumok, amelyek nagy mennyiségű memóriát használnak fel, vagy kevés erőforrást használnak fel. Nagy felügyelt objektumhivatkozásokat rendeljen null-hoz, hogy nagyobb valószínűséggel legyenek elérhetetlenebbek. Ez gyorsabban felszabadítja őket, mintha nemdeterminisztikus módon lettek volna visszanyerve.
  • Nem felügyelt erőforrásokat felszabadító blokk. Ez a blokk a disposing paraméter értékétől függetlenül fut.

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.

Az eldobási minta implementálása

Minden nem lezárt osztályt (vagy nem NotInheritablemódosított Visual 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ő metódusokat kell hozzáadnia az osztályhoz:

  • Egy Dispose implementáció, amely meghívja a Dispose(bool) metódust.
  • Egy Dispose(bool) metódus, amely elvégzi a tényleges törlést.
  • Ha az osztály kezeletlen erőforrásokkal foglalkozik, vagy biztosítson felülbírálást a Object.Finalize metódushoz, vagy csomagolja be a kezeletlen erőforrást egy SafeHandle-be.

Fontos

Véglegesítőre (Object.Finalize felülbírálásra) csak akkor van szükség, ha közvetlenül felügyelet nélküli erőforrásokra hivatkozik. Ez egy rendkívül fejlett forgatókönyv, amely általában elkerülhető:

  • Ha az osztály csak felügyelt objektumokrahivatkozik, az osztály továbbra is megvalósíthatja a megsemmisítési mintát. Nincs szükség véglegesítő implementálására.
  • Ha felügyelet nélküli erőforrásokkalkell foglalkoznia, erősen javasoljuk, hogy a felügyelet nélküli IntPtr leírót csomagolja be egy SafeHandle. A SafeHandle véglegesítőt biztosít, hogy ne kelljen saját magának írnia egyet. További információért lásd a Biztonságos fogantyúk bekezdést.

Alaposztály felügyelt erőforrásokkal

Íme egy általános példa egy olyan alaposztály elidegenítési mintájának implementálására, amely csak felügyelt erőforrásokat birtokol.

using System;
using System.IO;

public class DisposableBase : IDisposable
{
    // Detect redundant Dispose() calls.
    private bool _isDisposed;

    // Instantiate a disposable object owned by this class.
    private Stream? _managedResource = new MemoryStream();

    // 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 (!_isDisposed)
        {
            _isDisposed = true;

            if (disposing)
            {
                // Dispose managed state.
                _managedResource?.Dispose();
                _managedResource = null;
            }
        }
    }
}
Imports System.IO

Public Class DisposableBase
    Implements IDisposable

    ' Detect redundant Dispose() calls.
    Private _isDisposed As Boolean

    ' Instantiate a disposable object owned by this class.
    Private _managedResource As Stream = New MemoryStream()

    ' 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(disposing As Boolean)
        If Not _isDisposed Then
            _isDisposed = True

            If disposing Then
                ' Dispose managed state.
                _managedResource?.Dispose()
                _managedResource = Nothing
            End If
        End If
    End Sub
End Class

Jegyzet

Az előző példa egy próbabábuMemoryStream objektumot használ a minta szemléltetésére. Ehelyett bármilyen IDisposable használható.

Alaposztály nem felügyelt erőforrásokkal

Íme egy példa egy alaposztály kiürítési mintájának implementálására, amely felülbírálja a Object.Finalize-t annak érdekében, hogy megtisztítsa a nem felügyelt erőforrásokat, amelyekkel rendelkezik. A példa azt is bemutatja, hogyan implementálhat Dispose(bool) szálbiztos módon. A szinkronizálás kritikus fontosságú lehet a többszálas alkalmazások nem felügyelt erőforrásainak kezelésekor. Ahogy korábban említettük, ez egy speciális forgatókönyv.

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;

public class DisposableBaseWithFinalizer : IDisposable
{
    // Detect redundant Dispose() calls in a thread-safe manner.
    // _isDisposed == 0 means Dispose(bool) has not been called yet.
    // _isDisposed == 1 means Dispose(bool) has been already called.
    private int _isDisposed;

    // Instantiate a disposable object owned by this class.
    private Stream? _managedResource = new MemoryStream();

    // A pointer to 10 bytes allocated on the unmanaged heap.
    private IntPtr _unmanagedResource = Marshal.AllocHGlobal(10);

    ~DisposableBaseWithFinalizer() => 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)
    {
        // In case _isDisposed is 0, atomically set it to 1.
        // Enter the branch only if the original value is 0.
        if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
        {
            if (disposing)
            {
                _managedResource?.Dispose();
                _managedResource = null;
            }

            Marshal.FreeHGlobal(_unmanagedResource);
        }
    }
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Threading

Public Class DisposableBaseWithFinalizer
    Implements IDisposable

    ' Detect redundant Dispose() calls in a thread-safe manner.
    ' _isDisposed == 0 means Dispose(bool) has not been called yet.
    ' _isDisposed == 1 means Dispose(bool) has been already called.
    Private _isDisposed As Integer

    ' Instantiate a disposable object owned by this class.
    Private _managedResource As Stream = New MemoryStream()

    ' A pointer to 10 bytes allocated on the unmanaged heap.
    Private _unmanagedResource As IntPtr = Marshal.AllocHGlobal(10)

    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(disposing As Boolean)
        ' In case _isDisposed is 0, atomically set it to 1.
        ' Enter the branch only if the original value is 0.
        If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 Then
            If disposing Then
                _managedResource?.Dispose()
                _managedResource = Nothing
            End If

            Marshal.FreeHGlobal(_unmanagedResource)
        End If
    End Sub
End Class

Jegyzet

  • Az előző példa a AllocHGlobal használatával foglal le 10 bájtot a konstruktorban lévő nem felügyelt halomban, és felszabadítja a puffert a Dispose(bool)-ben a FreeHGlobalmeghívásával. Ez egy illusztrációs célú próba-hozzárendelés.
  • Ismét javasoljuk, hogy kerülje a véglegesítő implementálását. Lásd: Az eldobási minta implementálása egyéni biztonságos kezelővel az előző példának megfelelően, amely a nem determinisztikus véglegesítést és szinkronizálást SafeHandle-nek delegálja.

Borravaló

A C#-ban a véglegesítést a véglegesítőmegadásával valósítja meg, nem pedig a Object.Finalizefelülírásával. A Visual Basicben létrehozhat egy véglegesítőt Protected Overrides Sub Finalize().

A származtatott osztály eltávolítási mintájának implementálása

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

  • Egy 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 meg kell hívnia a base.Dispose(bool) (MyBase.Dispose(bool) a Visual Basicben) metódust is, amely argumentumként adja át a dekontribúciós állapotot (bool disposing paramétert).
  • Vagy egy SafeHandle osztályból származtatott osztály, amely a nem felügyelt erőforrást burkolja (ajánlott), vagy a Object.Finalize metódus felülbírálása. A SafeHandle osztály biztosít egy véglegesítőt, amely megszabadít attól, hogy saját magadnak kelljen megírnod egyet. Ha véglegesítő függvényt ad meg, a Dispose(bool) túlterhelést kell meghívnia false argumentummal.

Íme egy példa a biztonságos fogantyút használó származtatott osztály felszabadítási mintájának megvalósítására szolgáló általános sablonra:

using System.IO;

public class DisposableDerived : DisposableBase
{
    // To detect redundant calls
    private bool _isDisposed;

    // Instantiate a disposable object owned by this class.
    private Stream? _managedResource = new MemoryStream();

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            _isDisposed = true;

            if (disposing)
            {
                _managedResource?.Dispose();
                _managedResource = null;
            }
        }

        // Call base class implementation.
        base.Dispose(disposing);
    }
}
Imports System.IO

Public Class DisposableDerived
    Inherits DisposableBase

    ' To detect redundant calls
    Private _isDisposed As Boolean

    ' Instantiate a disposable object owned by this class.
    Private _managedResource As Stream = New MemoryStream()

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        If Not _isDisposed Then
            _isDisposed = True

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

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

Jegyzet

Az előző példa egy SafeFileHandle objektummal szemlélteti a mintát; bármely SafeHandle-ből származó objektum is használható. Vegye figyelembe, hogy a példa nem hozza létre megfelelően a SafeFileHandle objektumot.

Itt van az általános minta a dispose minta implementálásához egy olyan származtatott osztályban, amely felülírja a Object.Finalize-t.

using System.Threading;

public class DisposableDerivedWithFinalizer : DisposableBaseWithFinalizer
{
    // Detect redundant Dispose() calls in a thread-safe manner.
    // _isDisposed == 0 means Dispose(bool) has not been called yet.
    // _isDisposed == 1 means Dispose(bool) has been already called.
    private int _isDisposed;

    ~DisposableDerivedWithFinalizer() => Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        // In case _isDisposed is 0, atomically set it to 1.
        // Enter the branch only if the original value is 0.
        if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
        {
            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.
        }

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
Imports System.Threading

Public Class DisposableDerivedWithFinalizer
    Inherits DisposableBaseWithFinalizer

    ' Detect redundant Dispose() calls in a thread-safe manner.
    ' _isDisposed == 0 means Dispose(bool) has not been called yet.
    ' _isDisposed == 1 means Dispose(bool) has been already called.
    Private _isDisposed As Integer

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

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        ' In case _isDisposed is 0, atomically set it to 1.
        ' Enter the branch only if the original value is 0.
        If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 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.
        End If

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

Biztonságos 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 véglegesítő helyett System.Runtime.InteropServices.SafeHandle objektumokat hozzon létre.

A System.Runtime.InteropServices.SafeHandle egy absztrakt felügyelt típus, amely egy nem felügyelt erőforrást azonosító System.IntPtr-et becsomagol. Windows rendszeren azonosíthat egy kezelőt, Unix rendszeren pedig egy fájlleírót. A SafeHandle biztosítja az erőforrás egyszeri és egyszeri kiadásának biztosításához szükséges összes logikát, akár a SafeHandle eltávolításakor, akár a SafeHandle összes hivatkozásának elvetésekor, illetve a SafeHandle példány véglegesítésekor.

A System.Runtime.InteropServices.SafeHandle egy absztrakt alaposztály. A származtatott osztályok különféle típusú kezelők számára biztosítanak meghatározott példányokat. Ezek a származtatott osztályok ellenőrzik, hogy a System.IntPtr mely értékei tekinthetők érvénytelennek, és hogyan szabadítható fel ténylegesen a fogantyú. A SafeFileHandle például a SafeHandle alapján becsomagolja a nyitott fájlleírókat azonosító IntPtrs, és felülírja annak SafeHandle.ReleaseHandle() metódusát a bezáráshoz (Unix esetén a close függvényen, Windows esetén a CloseHandle függvényen keresztül). A .NET-kódtárakban lévő API-k többsége, amelyek nem felügyelt erőforrást hoznak létre, becsomagolják azt egy SafeHandle-ba, és szükség szerint visszaadják Önnek a SafeHandle-et, a nyers mutató átadása helyett. Olyan helyzetekben, amikor egy nem felügyelt összetevővel lép kapcsolatba és kap egy nem felügyelt erőforrást IntPtr azonosítóval, létrehozhat egy saját SafeHandle típust a burkoláshoz. Ennek eredményeképpen kevés nemSafeHandle típusnak 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 némelyike SafeHandle objektum lehet.

Az eldobási minta implementálása egyedi biztonságos fogantyúval

Az alábbi kód bemutatja, hogyan kezelhetők a nem felügyelt erőforrások egy SafeHandleimplementálásával.

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

// Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
class LocalAllocHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    private LocalAllocHandle() : base(ownsHandle: true) { }

    // No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
    protected override bool ReleaseHandle()
    {
        Marshal.FreeHGlobal(handle);
        return true;
    }

    // Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
    public static LocalAllocHandle Allocate(int numberOfBytes)
    {
        IntPtr nativeHandle = Marshal.AllocHGlobal(numberOfBytes);
        LocalAllocHandle safeHandle = new LocalAllocHandle();
        safeHandle.SetHandle(nativeHandle);
        return safeHandle;
    }
}

public class DisposableBaseWithSafeHandle : IDisposable
{
    // Detect redundant Dispose() calls.
    private bool _isDisposed;

    // Managed disposable objects owned by this class
    private LocalAllocHandle? _safeHandle = LocalAllocHandle.Allocate(10);
    private Stream? _otherUnmanagedResource = new MemoryStream();

    // 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 (!_isDisposed)
        {
            _isDisposed = true;

            if (disposing)
            {
                // Dispose managed state.
                _otherUnmanagedResource?.Dispose();
                _safeHandle?.Dispose();
                _otherUnmanagedResource = null;
                _safeHandle = null;
            }
        }
    }
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports Microsoft.Win32.SafeHandles

' Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
Public Class LocalAllocHandle
    Inherits SafeHandleZeroOrMinusOneIsInvalid

    Private Sub New()
        MyBase.New(True)
    End Sub

    ' No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
    Protected Overrides Function ReleaseHandle() As Boolean
        Marshal.FreeHGlobal(handle)
        Return True
    End Function

    ' Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
    Public Shared Function Allocate(numberOfBytes As Integer) As LocalAllocHandle
        Dim nativeHandle As IntPtr = Marshal.AllocHGlobal(numberOfBytes)
        Dim safeHandle As New LocalAllocHandle()
        safeHandle.SetHandle(nativeHandle)
        Return safeHandle
    End Function
End Class

Public Class DisposableBaseWithSafeHandle
    Implements IDisposable

    ' Detect redundant Dispose() calls.
    Private _isDisposed As Boolean

    ' Managed disposable objects owned by this class
    Private _safeHandle As LocalAllocHandle = LocalAllocHandle.Allocate(10)
    Private _otherUnmanagedResource As Stream = New MemoryStream()

    ' 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(disposing As Boolean)
        If Not _isDisposed Then
            _isDisposed = True

            If disposing Then
                ' Dispose managed state.
                _otherUnmanagedResource?.Dispose()
                _safeHandle?.Dispose()
                _otherUnmanagedResource = Nothing
                _safeHandle = Nothing
            End If
        End If
    End Sub
End Class

Jegyzet

A DisposableBaseWithSafeHandle osztály viselkedése egyenértékű a DisposableBaseWithFinalizer osztály viselkedésével egy korábbi példában, de az itt bemutatott megközelítés biztonságosabb:

  • Nincs szükség véglegesítő implementálására, mert SafeHandle gondoskodik a véglegesítésről.
  • A szálbiztonság biztosításához nincs szükség szinkronizálásra. Annak ellenére, hogy versenyfeltétel áll fenn a DisposableBaseWithSafeHandleDispose megvalósításában, SafeHandle garantálja, hogy SafeHandle.ReleaseHandle csak egyszer lesz meghívva.

Beépített biztonságos fogópontok a .NET-ben

A Microsoft.Win32.SafeHandles névtér következő származtatott osztályok biztonságos fogantyú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 Rendszerleíró kulcsok
SafeWaitHandle Várakozási fogópontok

Lásd még: