Implementacja metody DisposeImplement a Dispose method

Implementowanie Dispose metody jest przede wszystkim przeznaczone do zwalniania niezarządzanych zasobów.Implementing the Dispose method is primarily for releasing unmanaged resources. Podczas pracy z elementami członkowskimi wystąpień IDisposable , które są implementacjami, często są to wywołania kaskadowe Dispose .When working with instance members that are IDisposable implementations, it's common to cascade Dispose calls. Istnieją dodatkowe powody wdrożenia Dispose , na przykład w celu zwolnienia pamięci, która została przypisana, usunąć element, który został dodany do kolekcji lub sygnalizować wykorzystaną blokadę.There are additional reasons for implementing Dispose, for example, to free memory that was allocated, remove an item that was added to a collection, or signal the release of a lock that was acquired.

Moduł wyrzucania elementów bezużytecznych platformy .NET nie przydziela lub nie zwalnia pamięci niezarządzanej.The .NET garbage collector does not allocate or release unmanaged memory. Wzorzec do usuwania obiektu, nazywany wzorcem usuwania, nakłada kolejność na okres istnienia obiektu.The pattern for disposing an object, referred to as the dispose pattern, imposes order on the lifetime of an object. Wzorzec Dispose jest używany dla obiektów implementujących IDisposable interfejs i jest powszechny w przypadku współpracy z uchwytami plików i potoków, dojściami do rejestru, dojściami oczekiwania lub wskaźnikami do bloków pamięci niezarządzanej.The dispose pattern is used for objects that implement the IDisposable interface, and is common when interacting with file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. Dzieje się tak, ponieważ moduł wyrzucania elementów bezużytecznych nie może odzyskiwać obiektów niezarządzanych.This is because the garbage collector is unable to reclaim unmanaged objects.

Aby zapewnić, że zasoby są zawsze odpowiednio czyszczone, Dispose Metoda powinna być idempotentne, tak że jest ona wielokrotnie wywoływana bez zgłaszania wyjątku.To help ensure that resources are always cleaned up appropriately, a Dispose method should be idempotent, such that it is callable multiple times without throwing an exception. Ponadto kolejne wywołania elementu Dispose nie powinny nic robić.Furthermore, subsequent invocations of Dispose should do nothing.

Przykład kodu podany dla GC.KeepAlive metody pokazuje, jak wyrzucanie elementów bezużytecznych może spowodować uruchomienie finalizatora, podczas gdy niezarządzane odwołanie do obiektu lub jego członków jest nadal w użyciu.The code example provided for the GC.KeepAlive method shows how garbage collection can cause a finalizer to run, while an unmanaged reference to the object or its members is still in use. Warto mieć sens, GC.KeepAlive Aby obiekt nieuprawniony do wyrzucania elementów bezużytecznych od początku bieżącej procedury do punktu, w którym ta metoda jest wywoływana.It may make sense to utilize GC.KeepAlive to make the object ineligible for garbage collection from the start of the current routine to the point where this method is called.

Bezpieczne dojściaSafe handles

Pisanie kodu dla finalizatora obiektu to złożone zadanie, które może powodować problemy, jeśli nie zostanie wykonane prawidłowo.Writing code for an object's finalizer is a complex task that can cause problems if not done correctly. Dlatego zalecamy konstruowanie System.Runtime.InteropServices.SafeHandle obiektów zamiast implementowania finalizatora.Therefore, we recommend that you construct System.Runtime.InteropServices.SafeHandle objects instead of implementing a finalizer.

A System.Runtime.InteropServices.SafeHandle jest abstrakcyjnym typem zarządzanym, który otacza obiekt System.IntPtr , który identyfikuje niezarządzany zasób.A System.Runtime.InteropServices.SafeHandle is an abstract managed type that wraps an System.IntPtr that identifies an unmanaged resource. W systemie Windows może on identyfikować dojście w systemie UNIX, deskryptor pliku.On Windows it might identify a handle while on Unix, a file descriptor. Zapewnia ona wszystkie wymagane logiki, aby zapewnić, że ten zasób jest wystawiony raz i tylko raz, gdy SafeHandle zostanie usunięty lub gdy wszystkie odwołania do SafeHandle zostały usunięte, a SafeHandle wystąpienie zostanie sfinalizowane.It provides all of the logic necessary to ensure that this resource is released once and only once, when the SafeHandle is disposed of or when all references to the SafeHandle have been dropped and the SafeHandle instance is finalized.

System.Runtime.InteropServices.SafeHandleJest abstrakcyjną klasą bazową.The System.Runtime.InteropServices.SafeHandle is an abstract base class. Klasy pochodne zapewniają określone wystąpienia dla różnych rodzajów uchwytów.Derived classes provide specific instances for different kinds of handle. Te klasy pochodne sprawdzają, jakie wartości System.IntPtr są uważane za nieprawidłowe i jak ostatecznie zwolnić dojście.These derived classes validate what values for the System.IntPtr are considered invalid and how to actually free the handle. Na przykład podaje SafeFileHandle SafeHandle się do zawijania, IntPtrs który identyfikuje otwarte dojścia do pliku/deskryptory, i zastępuje jego SafeHandle.ReleaseHandle() metodę, aby zamknąć (za pośrednictwem close funkcji w systemie UNIX lub CloseHandle funkcji systemu Windows).For example, SafeFileHandle derives from SafeHandle to wrap IntPtrs that identify open file handles/descriptors, and overrides its SafeHandle.ReleaseHandle() method to close it (via the close function on Unix or CloseHandle function on Windows). Większość interfejsów API w bibliotekach platformy .NET, które tworzą zasób niezarządzany, będzie otoczyć ją w SafeHandle i zwrócić SafeHandle do Ciebie w razie potrzeby, zamiast wycofać pierwotny wskaźnik.Most APIs in .NET libraries that create an unmanaged resource will wrap it in a SafeHandle and return that SafeHandle to you as needed, rather than handing back the raw pointer. W sytuacjach, w których można korzystać z niezarządzanego składnika i uzyskać IntPtr dla niezarządzanego zasobu, można utworzyć własny SafeHandle Typ, aby go otoczyć.In situations where you interact with an unmanaged component and get an IntPtr for an unmanaged resource, you can create your own SafeHandle type to wrap it. W związku z SafeHandle tym kilka typów niewymagających zaimplementowania finalizatorów; Większość implementacji wzorców jednorazowych kończy Zawijanie innych zarządzanych zasobów, z których część może być SafeHandle s.As a result, few non-SafeHandle types need to implement finalizers; most disposable pattern implementations only end up wrapping other managed resources, some of which may be SafeHandles.

Następujące klasy pochodne w Microsoft.Win32.SafeHandles przestrzeni nazw zapewniają bezpieczne dojścia:The following derived classes in the Microsoft.Win32.SafeHandles namespace provide safe handles:

Dispose () i Dispose (bool)Dispose() and Dispose(bool)

IDisposableInterfejs wymaga implementacji pojedynczej metody bez parametrów Dispose .The IDisposable interface requires the implementation of a single parameterless method, Dispose. Ponadto każda klasa Niezapieczętowana powinna mieć dodatkową Dispose(bool) metodę przeciążenia, która ma być zaimplementowana:Also, any non-sealed class should have an additional Dispose(bool) overload method to be implemented:

  • publicImplementacja niewirtualna ( NonInheritable w Visual Basic) IDisposable.Dispose , która nie ma parametrów.A public non-virtual (NonInheritable in Visual Basic) IDisposable.Dispose implementation that has no parameters.

  • protected virtualMetoda ( Overridable w Visual Basic), Dispose której sygnatura:A protected virtual (Overridable in Visual Basic) Dispose method whose signature is:

    protected virtual void Dispose(bool disposing)
    {
    }
    
    Protected Overridable Sub Dispose(disposing As Boolean)
    End Sub
    

    Ważne

    disposingParametr powinien być false wywoływany z finalizatora i true wywoływany z IDisposable.Dispose metody.The disposing parameter should be false when called from a finalizer, and true when called from the IDisposable.Dispose method. Innymi słowy, jest to w true przypadku niejednoznacznego wywoływania i w false przypadku, gdy jest wywoływana niejednoznacznie.In other words, it is true when deterministically called and false when non-deterministically called.

Metoda Dispose ()The Dispose() method

Ponieważ public , niewirtualne ( NonInheritable w Visual Basic), Metoda bez parametrów Dispose jest wywoływana przez odbiorcę typu, jego celem jest zwolnienie niezarządzanych zasobów, wykonanie ogólnego oczyszczania i wskazanie, że finalizator, jeśli taki istnieje, nie musi być uruchamiany.Because the public, non-virtual (NonInheritable in Visual Basic), parameterless Dispose method is called by a consumer of the type, its purpose is to free unmanaged resources, perform general cleanup, and to indicate that the finalizer, if one is present, doesn't have to run. Zwalnianie rzeczywistej pamięci skojarzonej z obiektem zarządzanym jest zawsze domeną modułu wyrzucania elementów bezużytecznych.Freeing the actual memory associated with a managed object is always the domain of the garbage collector. Z tego powodu ma standardową implementację:Because of this, it has a standard implementation:

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

DisposeMetoda wykonuje wszystkie operacje czyszczenia obiektów, dlatego Moduł wyrzucania elementów bezużytecznych nie musi już wywoływać Object.Finalize przesłonięcia obiektów.The Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects' Object.Finalize override. W związku z tym wywołanie SuppressFinalize metody uniemożliwia uruchomienie finalizatora przez moduł wyrzucania elementów bezużytecznych.Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. Jeśli typ nie ma finalizatora, wywołanie nie GC.SuppressFinalize ma żadnego wpływu.If the type has no finalizer, the call to GC.SuppressFinalize has no effect. Należy zauważyć, że rzeczywiste oczyszczanie jest wykonywane przez Dispose(bool) Przeciążenie metody.Note that the actual cleanup is performed by the Dispose(bool) method overload.

Przeciążenie metody Dispose (bool)The Dispose(bool) method overload

W przypadku przeciążenia disposing parametr jest Boolean wskazuje, czy wywołanie metody pochodzi z Dispose metody (jej wartość jest true ) czy z finalizatora (jego wartość to false ).In the overload, the disposing parameter is a Boolean that indicates whether the method call comes from a Dispose method (its value is true) or from a finalizer (its value is false).

Treść metody składa się z dwóch bloków kodu:The body of the method consists of two blocks of code:

  • Blok zwalniający niezarządzane zasoby.A block that frees unmanaged resources. Ten blok jest wykonywany niezależnie od wartości disposing parametru.This block executes regardless of the value of the disposing parameter.

  • Blok warunkowy zwalniający zarządzane zasoby.A conditional block that frees managed resources. Ten blok jest wykonywany, gdy wartość disposing jest true .This block executes if the value of disposing is true. Zarządzane zasoby, które zwalnia, to m.in.:The managed resources that it frees can include:

    • Obiekty zarządzane, które implementują IDisposable .Managed objects that implement IDisposable. Bloku warunkowego można użyć do wywołania ich Dispose implementacji (kaskadowego Dispose).The conditional block can be used to call their Dispose implementation (cascade dispose). Jeśli użyto klasy pochodnej System.Runtime.InteropServices.SafeHandle , aby otoczyć niezarządzany zasób, należy wywołać SafeHandle.Dispose() implementację tutaj.If you have used a derived class of System.Runtime.InteropServices.SafeHandle to wrap your unmanaged resource, you should call the SafeHandle.Dispose() implementation here.

    • Zarządzane obiekty, które zużywają duże ilości pamięci lub zużywają niewystarczające zasoby.Managed objects that consume large amounts of memory or consume scarce resources. Przypisz do nich duże odwołania do obiektów zarządzanych, aby null zwiększyć ich dostępność.Assign large managed object references to null to make them more likely to be unreachable. Te wersje są szybsze niż w przypadku, gdy były odzyskiwane w sposób Niedeterministyczny i zwykle są wykonywane poza blokiem warunkowym.This releases them faster than if they were reclaimed non-deterministically, and this is usually done outside of the conditional block.

Jeśli wywołanie metody pochodzi od finalizatora, należy wykonać tylko kod, który zwolni niezarządzane zasoby.If the method call comes from a finalizer, only the code that frees unmanaged resources should execute. Realizator jest odpowiedzialny za zapewnienie, że fałszywa ścieżka nie współdziała z zarządzanymi obiektami, które mogły zostać ododzyskiwane.The implementer is responsible for ensuring that the false path doesn't interact with managed objects that may have been reclaimed. Jest to ważne, ponieważ kolejność, w której moduł zbierający elementy bezużyteczne niszczy obiekty zarządzane podczas finalizowania, nie jest deterministyczna.This is important because the order in which the garbage collector destroys managed objects during finalization is non-deterministic.

Kaskadowe wywołania DisposeCascade dispose calls

Jeśli klasa jest własnością pola lub właściwości, a jej typ implementuje IDisposable , należy również zaimplementować zawierającą ją klasę IDisposable .If your class owns a field or property, and its type implements IDisposable, the containing class itself should also implement IDisposable. Klasa, która tworzy wystąpienie IDisposable implementacji i zapisuje ją jako element członkowski wystąpienia, jest również odpowiedzialna za jego czyszczenie.A class that instantiates an IDisposable implementation and storing it as an instance member, is also responsible for its cleanup. Jest to pomocne w zapewnieniu, że odwołania do typów jednorazowych, których dotyczy odwołanie, są nadawane za pomocą Dispose metody.This is to help ensure that the referenced disposable types are given the opportunity to deterministically perform clean up through the Dispose method. W tym przykładzie Klasa jest sealed (lub NotInheritable w Visual Basic).In this example, the class is sealed (or NotInheritable in Visual Basic).

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

Implementowanie wzorca usuwaniaImplement the dispose pattern

Wszystkie niezapieczętowane klasy lub (Visual Basic klas, które nie zostały zmodyfikowane jako NotInheritable ), powinny być uważane za potencjalną klasę bazową, ponieważ mogą być dziedziczone.All non-sealed classes or (Visual Basic classes not modified as NotInheritable) should be considered a potential base class, because they could be inherited. W przypadku zaimplementowania wzorca usuwania dla dowolnej potencjalnej klasy podstawowej należy podać następujące elementy:If you implement the dispose pattern for any potential base class, you must provide the following:

  • DisposeImplementacja, która wywołuje Dispose(bool) metodę.A Dispose implementation that calls the Dispose(bool) method.
  • Dispose(bool)Metoda, która wykonuje rzeczywiste oczyszczanie.A Dispose(bool) method that performs the actual cleanup.
  • Klasa pochodna, SafeHandle która otacza niezarządzany zasób (zalecane) lub przesłonięcie Object.Finalize metody.Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. SafeHandleKlasa zawiera finalizator, więc nie trzeba pisać siebie.The SafeHandle class provides a finalizer, so you do not have to write one yourself.

Ważne

Klasa bazowa może odwoływać się tylko do obiektów zarządzanych i zaimplementować wzorzec Dispose.It is possible for a base class to only reference managed objects, and implement the dispose pattern. W takich przypadkach finalizator jest niepotrzebny.In these cases, a finalizer is unnecessary. Finalizator jest wymagany tylko w przypadku bezpośredniego odwoływania się do zasobów niezarządzanych.A finalizer is only required if you directly reference unmanaged resources.

Oto ogólny wzorzec implementowania wzorca usuwania dla klasy bazowej, która używa bezpiecznego dojścia.Here's the general pattern for implementing the dispose pattern for a base class that uses a safe handle.

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

class BaseClass : IDisposable
{
    // To detect redundant calls
    private bool _disposed = false;

    // 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);

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

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

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

Class BaseClass : Implements IDisposable
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False
    ' Instantiate a SafeHandle instance.
    Dim handle 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(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            handle.Dispose()
            ' Free any other managed objects here.
            '
        End If

        disposed = True
    End Sub
End Class

Uwaga

W poprzednim przykładzie użyto SafeFileHandle obiektu do zilustrowania wzorca; zamiast tego można użyć dowolnego obiektu pochodnego SafeHandle .The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Należy zauważyć, że przykład nie tworzy prawidłowo wystąpienia SafeFileHandle obiektu.Note that the example does not properly instantiate its SafeFileHandle object.

Oto ogólny wzorzec implementowania wzorca usuwania dla klasy bazowej, która zastąpi Object.Finalize .Here's the general pattern for implementing the dispose pattern for a base class that overrides Object.Finalize.

using System;

class BaseClass : IDisposable
{
    // To detect redundant calls
    private bool _disposed = false;

    ~BaseClass() => 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 (_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;
    }
}
Class BaseClass : Implements IDisposable
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False

    ' 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 disposed Then Return

        If disposing Then
            ' Free any other managed objects here.
            '
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True
    End Sub

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

Porada

W języku C# tworzysz finalizator przez zastąpienie Object.Finalize .In C#, you create a finalizer by overriding Object.Finalize. W Visual Basic jest to realizowane za pomocą Protected Overrides Sub Finalize() .In Visual Basic, this is done with Protected Overrides Sub Finalize().

Implementowanie wzorca usuwania dla klasy pochodnejImplement the dispose pattern for a derived class

Klasa pochodna klasy, która implementuje IDisposable Interfejs IDisposable , nie należy implementować, ponieważ Implementacja klasy bazowej IDisposable.Dispose jest dziedziczona przez klasy pochodne.A class derived from a class that implements the IDisposable interface shouldn't implement IDisposable, because the base class implementation of IDisposable.Dispose is inherited by its derived classes. Zamiast tego, aby oczyścić klasę pochodną, należy podać następujące elementy:Instead, to cleanup a derived class, you provide the following:

  • protected override void Dispose(bool)Metoda, która zastępuje metodę klasy bazowej i wykonuje rzeczywiste oczyszczanie klasy pochodnej.A protected override void Dispose(bool) method that overrides the base class method and performs the actual cleanup of the derived class. Ta metoda musi również wywołać base.Dispose(bool) metodę ( MyBase.Dispose(bool) w Visual Basic) klasy bazowej i przekazać jej stan likwidacji dla tego argumentu.This method must also call the base.Dispose(bool) (MyBase.Dispose(bool) in Visual Basic) method of the base class and pass its disposing status for the argument.
  • Klasa pochodna, SafeHandle która otacza niezarządzany zasób (zalecane) lub przesłonięcie Object.Finalize metody.Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. SafeHandleKlasa zawiera finalizator, który uwalnia Cię do kodu.The SafeHandle class provides a finalizer that frees you from having to code one. Jeśli podasz finalizator, musi on wywołać Dispose(bool) Przeciążenie z disposing argumentem false .If you do provide a finalizer, it must call the Dispose(bool) overload with a disposing argument of false.

Poniżej przedstawiono ogólny schemat implementowania wzorca usuwania dla klasy pochodnej, w którym jest używane bezpieczne dojście:Here's the general pattern for implementing the dispose pattern for a derived class that uses a safe handle:

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

class DerivedClass : BaseClass
{
    // To detect redundant calls
    private bool _disposed = false;

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

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

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

        _disposed = true;

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

Class DerivedClass : Inherits BaseClass
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False
    ' Instantiate a SafeHandle instance.
    Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            handle.Dispose()
            ' Free any other managed objects here.
            '
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True

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

Uwaga

W poprzednim przykładzie użyto SafeFileHandle obiektu do zilustrowania wzorca; zamiast tego można użyć dowolnego obiektu pochodnego SafeHandle .The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Należy zauważyć, że przykład nie tworzy prawidłowo wystąpienia SafeFileHandle obiektu.Note that the example does not properly instantiate its SafeFileHandle object.

Oto ogólny wzorzec implementowania wzorca usuwania dla klasy pochodnej, która zastąpi Object.Finalize :Here's the general pattern for implementing the dispose pattern for a derived class that overrides Object.Finalize:

using System;

class DerivedClass : BaseClass
{
    // To detect redundant calls
    bool _disposed = false;

    ~DerivedClass() => Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override 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;

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
Class DerivedClass : Inherits BaseClass
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            ' Free any other managed objects here.
            '
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True

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

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

Implementowanie wzorca Dispose z bezpiecznymi dojściamiImplement the dispose pattern with safe handles

Poniższy przykład ilustruje wzorzec Dispose dla klasy bazowej, DisposableStreamResource który używa bezpiecznego dojścia do hermetyzowania niezarządzanych zasobów.The following example illustrates the dispose pattern for a base class, DisposableStreamResource, that uses a safe handle to encapsulate unmanaged resources. Definiuje DisposableStreamResource klasę, która używa SafeFileHandle do zawijania Stream obiektu, który reprezentuje otwarty plik.It defines a DisposableStreamResource class that uses a SafeFileHandle to wrap a Stream object that represents an open file. Klasa zawiera również pojedynczą właściwość, Size która zwraca łączną liczbę bajtów w strumieniu pliku.The class also includes a single property, Size, that returns the total number of bytes in the file stream.

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

public class DisposableStreamResource : IDisposable
{
    // Define constants.
    protected const uint GENERIC_READ = 0x80000000;
    protected const uint FILE_SHARE_READ = 0x00000001;
    protected const uint OPEN_EXISTING = 3;
    protected const uint FILE_ATTRIBUTE_NORMAL = 0x80;
    private const int INVALID_FILE_SIZE = unchecked((int)0xFFFFFFFF);

    // Define Windows APIs.
    [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode)]
    protected static extern SafeFileHandle CreateFile(
        string lpFileName, uint dwDesiredAccess,
        uint dwShareMode, IntPtr lpSecurityAttributes,
        uint dwCreationDisposition, uint dwFlagsAndAttributes,
        IntPtr hTemplateFile);

    [DllImport("kernel32.dll")]
    private static extern int GetFileSize(
        SafeFileHandle hFile, out int lpFileSizeHigh);

    // Define locals.
    private bool _disposed = false;
    private readonly SafeFileHandle _safeHandle;
    private readonly int _upperWord;

    public DisposableStreamResource(string fileName)
    {
        if (string.IsNullOrWhiteSpace(fileName))
        {
            throw new ArgumentException("The fileName cannot be null or an empty string");
        }

        _safeHandle = CreateFile(
            fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

        // Get file size.
        Size = GetFileSize(_safeHandle, out _upperWord);
        if (Size == INVALID_FILE_SIZE)
        {
            Size = -1;
        }
        else if (_upperWord > 0)
        {
            Size = (((long)_upperWord) << 32) + Size;
        }
    }

    public long Size { get; }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

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

        // Dispose of managed resources here.
        if (disposing)
        {
            _safeHandle?.Dispose();
        }

        // Dispose of any unmanaged resources not wrapped in safe handles.

        _disposed = true;
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.IO

Public Class DisposableStreamResource : Implements IDisposable
    ' Define constants.
    Protected Const GENERIC_READ As UInteger = &H80000000UI
    Protected Const FILE_SHARE_READ As UInteger = &H0I
    Protected Const OPEN_EXISTING As UInteger = 3
    Protected Const FILE_ATTRIBUTE_NORMAL As UInteger = &H80
    Private Const INVALID_FILE_SIZE As Integer = &HFFFFFFFF

    ' Define Windows APIs.
    Protected Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (
        lpFileName As String, dwDesiredAccess As UInt32,
        dwShareMode As UInt32, lpSecurityAttributes As IntPtr,
        dwCreationDisposition As UInt32, dwFlagsAndAttributes As UInt32,
        hTemplateFile As IntPtr) As SafeFileHandle

    Private Declare Function GetFileSize Lib "kernel32" (
        hFile As SafeFileHandle, ByRef lpFileSizeHigh As Integer) As Integer

    ' Define locals.
    Private disposed As Boolean = False
    Private ReadOnly safeHandle As SafeFileHandle
    Private ReadOnly upperWord As Integer

    Public Sub New(fileName As String)
        If String.IsNullOrWhiteSpace(fileName) Then
            Throw New ArgumentNullException("The fileName cannot be null or an empty string")
        End If

        safeHandle = CreateFile(
            fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)

        ' Get file size.
        Size = GetFileSize(safeHandle, upperWord)
        If Size = INVALID_FILE_SIZE Then
            Size = -1
        ElseIf upperWord > 0 Then
            Size = (CLng(upperWord) << 32) + Size
        End If
    End Sub

    Public ReadOnly Property Size As Long

    Public Sub Dispose() _
              Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposed Then Exit Sub

        ' Dispose of managed resources here.
        If disposing Then
            safeHandle.Dispose()
        End If

        ' Dispose of any unmanaged resources not wrapped in safe handles.

        disposed = True
    End Sub
End Class

Implementowanie wzorca usuwania dla klasy pochodnej z bezpiecznymi dojściamiImplement the dispose pattern for a derived class with safe handles

Poniższy przykład ilustruje wzorzec usuwania dla klasy pochodnej, DisposableStreamResource2 która dziedziczy z DisposableStreamResource klasy przedstawionej w poprzednim przykładzie.The following example illustrates the dispose pattern for a derived class, DisposableStreamResource2, that inherits from the DisposableStreamResource class presented in the previous example. Klasa dodaje dodatkową metodę, WriteFileInfo i używa SafeFileHandle obiektu do zawijania uchwytu zapisywalnego pliku.The class adds an additional method, WriteFileInfo, and uses a SafeFileHandle object to wrap the handle of the writable file.

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

public class DisposableStreamResource2 : DisposableStreamResource
{
    // Define additional constants.
    protected const uint GENERIC_WRITE = 0x40000000;
    protected const uint OPEN_ALWAYS = 4;

    // Define additional APIs.
    [DllImport("kernel32.dll")]
    protected static extern bool WriteFile(
        SafeFileHandle safeHandle, string lpBuffer,
        int nNumberOfBytesToWrite, out int lpNumberOfBytesWritten,
        IntPtr lpOverlapped);

    // To detect redundant calls
    private bool _disposed = false;
    private bool _created = false;
    private SafeFileHandle _safeHandle;
    private readonly string _fileName;

    public DisposableStreamResource2(string fileName) : base(fileName) => _fileName = fileName;

    public void WriteFileInfo()
    {
        if (!_created)
        {
            _safeHandle = CreateFile(
                @".\FileInfo.txt", GENERIC_WRITE, 0, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

            _created = true;
        }

        string output = $"{_fileName}: {Size:N0} bytes\n";
        _ = WriteFile(_safeHandle, output, output.Length, out _, IntPtr.Zero);
    }

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

        // Release any managed resources here.
        if (disposing)
        {
            // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

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

        _disposed = true;

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

Public Class DisposableStreamResource2 : Inherits DisposableStreamResource
    ' Define additional constants.
    Protected Const GENERIC_WRITE As Integer = &H40000000
    Protected Const OPEN_ALWAYS As Integer = 4

    ' Define additional APIs.
    Protected Declare Function WriteFile Lib "kernel32.dll" (
        safeHandle As SafeFileHandle, lpBuffer As String,
        nNumberOfBytesToWrite As Integer, ByRef lpNumberOfBytesWritten As Integer,
        lpOverlapped As Object) As Boolean

    ' Define locals.
    Private disposed As Boolean = False
    Private created As Boolean = False
    Private safeHandle As SafeFileHandle
    Private ReadOnly filename As String

    Public Sub New(filename As String)
        MyBase.New(filename)
        Me.filename = filename
    End Sub

    Public Sub WriteFileInfo()
        If Not created Then
            safeHandle = CreateFile(
                ".\FileInfo.txt", GENERIC_WRITE, 0, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)
            created = True
        End If

        Dim output As String = $"{filename }: {Size:N0} bytes {vbCrLf }"
        Dim result = WriteFile(safeHandle, output, output.Length, 0&, Nothing)
    End Sub

    Protected Overridable Overloads Sub Dispose(disposing As Boolean)
        If disposed Then Exit Sub

        ' Release any managed resources here.
        If disposing Then
            safeHandle?.Dispose()
        End If

        disposed = True
        ' Release any unmanaged resources not wrapped by safe handles here.

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

Zobacz takżeSee also