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 SafeHandle
s.
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:
- SafeFileHandleKlasy, SafeMemoryMappedFileHandle , i SafePipeHandle , dla plików, plików mapowanych na pamięć i potoków.The SafeFileHandle, SafeMemoryMappedFileHandle, and SafePipeHandle class, for files, memory mapped files, and pipes.
- SafeMemoryMappedViewHandleKlasa dla widoków pamięci.The SafeMemoryMappedViewHandle class, for memory views.
- SafeNCryptKeyHandleKlasy, SafeNCryptProviderHandle i SafeNCryptSecretHandle , dla konstrukcji kryptografii.The SafeNCryptKeyHandle, SafeNCryptProviderHandle, and SafeNCryptSecretHandle classes, for cryptography constructs.
- SafeRegistryHandleKlasa dla kluczy rejestru.The SafeRegistryHandle class, for registry keys.
- SafeWaitHandleKlasa, dla uchwytów oczekiwania.The SafeWaitHandle class, for wait 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:
public
Implementacja niewirtualna (NonInheritable
w Visual Basic) IDisposable.Dispose , która nie ma parametrów.Apublic
non-virtual (NonInheritable
in Visual Basic) IDisposable.Dispose implementation that has no parameters.protected virtual
Metoda (Overridable
w Visual Basic),Dispose
której sygnatura:Aprotected 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
disposing
Parametr powinien byćfalse
wywoływany z finalizatora itrue
wywoływany z IDisposable.Dispose metody.Thedisposing
parameter should befalse
when called from a finalizer, andtrue
when called from the IDisposable.Dispose method. Innymi słowy, jest to wtrue
przypadku niejednoznacznego wywoływania i wfalse
przypadku, gdy jest wywoływana niejednoznacznie.In other words, it istrue
when deterministically called andfalse
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
Dispose
Metoda 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 thedisposing
parameter.Blok warunkowy zwalniający zarządzane zasoby.A conditional block that frees managed resources. Ten blok jest wykonywany, gdy wartość
disposing
jesttrue
.This block executes if the value ofdisposing
istrue
. 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 tonull
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 theDispose(bool)
method. Dispose(bool)
Metoda, która wykonuje rzeczywiste oczyszczanie.ADispose(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.Aprotected 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 thebase.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 zdisposing
argumentemfalse
.If you do provide a finalizer, it must call theDispose(bool)
overload with adisposing
argument offalse
.
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