Implementare un metodo DisposeImplement a Dispose method

L'implementazione del Dispose metodo riguarda principalmente il rilascio di risorse non gestite.Implementing the Dispose method is primarily for releasing unmanaged resources. Quando si lavora con membri di istanza che sono IDisposable implementazioni, le chiamate a cascata sono comuni Dispose .When working with instance members that are IDisposable implementations, it's common to cascade Dispose calls. Esistono altri motivi per implementare Dispose , ad esempio, per liberare la memoria allocata, rimuovere un elemento aggiunto a una raccolta o segnalare il rilascio di un blocco acquisito.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.

Il Garbage Collector .NET non alloca o rilascia la memoria non gestita.The .NET garbage collector does not allocate or release unmanaged memory. Il modello per l'eliminazione di un oggetto, definito come modello Dispose, impone un ordine per la durata di un oggetto.The pattern for disposing an object, referred to as the dispose pattern, imposes order on the lifetime of an object. Il modello Dispose viene usato per gli oggetti che implementano l' IDisposable interfaccia ed è comune quando si interagisce con handle di file e pipe, handle del registro di sistema, handle di attesa o puntatori a blocchi di memoria non gestita.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. Questo perché il Garbage Collector non è in grado di recuperare gli oggetti non gestiti.This is because the garbage collector is unable to reclaim unmanaged objects.

Per garantire che le risorse vengano sempre pulite correttamente, un Dispose metodo deve essere idempotente, in modo che sia possibile chiamarlo più volte senza generare un'eccezione.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. Inoltre, le chiamate successive di Dispose non devono eseguire alcuna operazione.Furthermore, subsequent invocations of Dispose should do nothing.

Nell'esempio di codice fornito per il GC.KeepAlive metodo viene illustrato come Garbage Collection possibile causare l'esecuzione di un finalizzatore, mentre un riferimento non gestito all'oggetto o ai relativi membri è ancora in uso.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. Potrebbe essere utile usare GC.KeepAlive per rendere l'oggetto non idoneo per Garbage Collection dall'inizio della routine corrente fino al punto in cui viene chiamato il metodo.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.

Handle sicuriSafe handles

La scrittura di codice per il finalizzatore di un oggetto è un'attività complessa che può causare problemi se non eseguita correttamente.Writing code for an object's finalizer is a complex task that can cause problems if not done correctly. È pertanto consigliabile costruire oggetti System.Runtime.InteropServices.SafeHandle anziché implementare un finalizzatore.Therefore, we recommend that you construct System.Runtime.InteropServices.SafeHandle objects instead of implementing a finalizer.

Un System.Runtime.InteropServices.SafeHandle è un tipo gestito astratto che esegue il wrapping di un oggetto System.IntPtr che identifica una risorsa non gestita.A System.Runtime.InteropServices.SafeHandle is an abstract managed type that wraps an System.IntPtr that identifies an unmanaged resource. In Windows è possibile che identifichi un handle in UNIX, un descrittore di file.On Windows it might identify a handle while on Unix, a file descriptor. Fornisce tutta la logica necessaria per garantire che questa risorsa venga rilasciata una sola volta, quando l'oggetto SafeHandle viene eliminato o quando tutti i riferimenti all'oggetto SafeHandle sono stati eliminati e l' SafeHandle istanza viene finalizzata.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.SafeHandleÈ una classe di base astratta.The System.Runtime.InteropServices.SafeHandle is an abstract base class. Le classi derivate forniscono istanze specifiche per diversi tipi di handle.Derived classes provide specific instances for different kinds of handle. Queste classi derivate convalidano quali valori per System.IntPtr sono considerati non validi e come effettivamente liberare l'handle.These derived classes validate what values for the System.IntPtr are considered invalid and how to actually free the handle. Ad esempio, SafeFileHandle deriva da SafeHandle a wrap IntPtrs che identificano gli handle di file aperti/descrittori ed esegue l'override SafeHandle.ReleaseHandle() del relativo metodo per chiuderlo (tramite la close funzione su UNIX o CloseHandle Function in 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). La maggior parte delle API nelle librerie .NET che creano una risorsa non gestita ne eseguirà il wrapping in un oggetto SafeHandle e lo restituirà in SafeHandle base alle esigenze, anziché tornare al puntatore non elaborato.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. Nelle situazioni in cui si interagisce con un componente non gestito e si ottiene un IntPtr per una risorsa non gestita, è possibile creare un SafeHandle tipo personalizzato per eseguire il wrapping.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. Di conseguenza, pochi SafeHandle tipi non devono implementare i finalizzatori. la maggior parte delle implementazioni di modelli Disposable finirà solo con il wrapping di altre risorse gestite, alcune delle quali possono essere SafeHandle .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.

Le seguenti classi derivate nello spazio dei nomi Microsoft.Win32.SafeHandles forniscono handle sicuri:The following derived classes in the Microsoft.Win32.SafeHandles namespace provide safe handles:

Dispose () e Dispose (bool)Dispose() and Dispose(bool)

L'interfaccia IDisposable richiede l'implementazione di un singolo metodo senza parametri, Dispose.The IDisposable interface requires the implementation of a single parameterless method, Dispose. Inoltre, qualsiasi classe non sealed deve avere un Dispose(bool) metodo di overload aggiuntivo da implementare:Also, any non-sealed class should have an additional Dispose(bool) overload method to be implemented:

  • publicImplementazione non virtuale ( NonInheritable in Visual Basic) priva di IDisposable.Dispose parametri.A public non-virtual (NonInheritable in Visual Basic) IDisposable.Dispose implementation that has no parameters.

  • protected virtualMetodo ( Overridable in Visual Basic) la Dispose cui firma è: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
    

    Importante

    Il disposing parametro deve essere false chiamato da un finalizzatore e quando viene true chiamato dal IDisposable.Dispose metodo.The disposing parameter should be false when called from a finalizer, and true when called from the IDisposable.Dispose method. In altre parole, è quando viene chiamato in modo true deterministico e quando viene chiamato in modo false non deterministico.In other words, it is true when deterministically called and false when non-deterministically called.

Metodo Dispose ()The Dispose() method

Poiché la proprietà public , non virtuale ( NonInheritable in Visual Basic), il metodo senza parametri Dispose viene chiamato da un consumer del tipo, il suo scopo è liberare le risorse non gestite, eseguire una pulizia generale e indicare che il finalizzatore, se presente, non è necessario eseguire.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. La liberazione della memoria effettiva associata a un oggetto gestito è sempre il dominio del Garbage Collector.Freeing the actual memory associated with a managed object is always the domain of the garbage collector. Per questo motivo il metodo ha un'implementazione standard: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

Il metodo Dispose esegue la pulizia di tutti gli oggetti, quindi il Garbage Collector non deve più chiamare l'override Object.Finalize degli oggetti.The Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects' Object.Finalize override. Pertanto, la chiamata al metodo SuppressFinalize impedisce al Garbage Collector di eseguire il finalizzatore.Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. Se il tipo non dispone di un finalizzatore, la chiamata a GC.SuppressFinalize non ha alcun effetto.If the type has no finalizer, the call to GC.SuppressFinalize has no effect. Si noti che l'effettiva pulizia viene eseguita dall' Dispose(bool) Overload del metodo.Note that the actual cleanup is performed by the Dispose(bool) method overload.

Overload del metodo Dispose (bool)The Dispose(bool) method overload

Nell'overload, il disposing parametro è un oggetto Boolean che indica se la chiamata al metodo deriva da un Dispose Metodo (il valore è true ) o da un finalizzatore (il valore è 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).

Il corpo del metodo è costituito da due blocchi di codice:The body of the method consists of two blocks of code:

  • Un blocco che libera le risorse non gestite.A block that frees unmanaged resources. Questo blocco viene eseguito indipendentemente dal valore del parametro disposing.This block executes regardless of the value of the disposing parameter.

  • Un blocco condizionale che libera le risorse gestite.A conditional block that frees managed resources. Il blocco è eseguito se il valore di disposing è true.This block executes if the value of disposing is true. Le risorse gestite liberate possono includere:The managed resources that it frees can include:

    • Oggetti gestiti che implementano IDisposable.Managed objects that implement IDisposable. Il blocco condizionale può essere usato per chiamare la relativa Dispose implementazione (Cascade Dispose).The conditional block can be used to call their Dispose implementation (cascade dispose). Se è stata usata una classe derivata di System.Runtime.InteropServices.SafeHandle per eseguire il wrapping della risorsa non gestita, è necessario chiamare l' SafeHandle.Dispose() implementazione qui.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.

    • Oggetti gestiti che usano grandi quantità di memoria o risorse insufficienti.Managed objects that consume large amounts of memory or consume scarce resources. Assegnare riferimenti a oggetti gestiti di grandi dimensioni a null per renderli più probabilmente irraggiungibili.Assign large managed object references to null to make them more likely to be unreachable. Questa operazione viene rilasciata più velocemente rispetto a quando vengono recuperate in modo non deterministico e in genere viene eseguita all'esterno del blocco condizionale.This releases them faster than if they were reclaimed non-deterministically, and this is usually done outside of the conditional block.

Se la chiamata al metodo deriva da un finalizzatore, deve essere eseguito solo il codice che libera le risorse non gestite.If the method call comes from a finalizer, only the code that frees unmanaged resources should execute. L'implementatore è responsabile di garantire che il percorso false non interagisca con oggetti gestiti che potrebbero essere stati recuperati.The implementer is responsible for ensuring that the false path doesn't interact with managed objects that may have been reclaimed. Questo è importante perché l'ordine in cui il Garbage Collector elimina gli oggetti gestiti durante la finalizzazione non è deterministico.This is important because the order in which the garbage collector destroys managed objects during finalization is non-deterministic.

Chiamate a Dispose a catenaCascade dispose calls

Se la classe è proprietaria di un campo o di una proprietà e il tipo implementa IDisposable , anche la classe che lo contiene deve implementare IDisposable .If your class owns a field or property, and its type implements IDisposable, the containing class itself should also implement IDisposable. Una classe che crea un'istanza di un' IDisposable implementazione di e la archivia come membro di istanza è anche responsabile della pulizia.A class that instantiates an IDisposable implementation and storing it as an instance member, is also responsible for its cleanup. Ciò consente di garantire che ai tipi Disposable a cui viene fatto riferimento venga data la possibilità di eseguire la pulizia in modo deterministico tramite il Dispose metodo.This is to help ensure that the referenced disposable types are given the opportunity to deterministically perform clean up through the Dispose method. In questo esempio la classe è sealed (o NotInheritable in 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

Implementare il modello DisposeImplement the dispose pattern

Tutte le classi non sealed o (Visual Basic classi non modificate come NotInheritable ) devono essere considerate come una classe di base potenziale, perché potrebbero essere ereditate.All non-sealed classes or (Visual Basic classes not modified as NotInheritable) should be considered a potential base class, because they could be inherited. Se si implementa il modello Dispose per qualsiasi classe di base potenziale, è necessario specificare quanto segue:If you implement the dispose pattern for any potential base class, you must provide the following:

  • Un'implementazione Dispose che chiami il metodo Dispose(bool).A Dispose implementation that calls the Dispose(bool) method.
  • Dispose(bool)Metodo che esegue la pulizia effettiva.A Dispose(bool) method that performs the actual cleanup.
  • Una classe derivata da SafeHandle che esegua il wrapping della risorsa non gestita (consigliato) o un override al metodo Object.Finalize.Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. La SafeHandle classe fornisce un finalizzatore, quindi non è necessario scriverne uno manualmente.The SafeHandle class provides a finalizer, so you do not have to write one yourself.

Importante

Una classe di base può fare riferimento solo a oggetti gestiti e implementare il modello Dispose.It is possible for a base class to only reference managed objects, and implement the dispose pattern. In questi casi, un finalizzatore non è necessario.In these cases, a finalizer is unnecessary. Un finalizzatore è necessario solo se si fa riferimento direttamente A risorse non gestite.A finalizer is only required if you directly reference unmanaged resources.

Di seguito è illustrato il modello generale per implementare il modello Dispose per una classe di base che usa un handle sicuro.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

Nota

L'esempio precedente usa un oggetto SafeFileHandle per illustrato il criterio; sarebbe possibile usare invece qualsiasi oggetto derivato da SafeHandle.The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Si noti che l'esempio non crea correttamente un'istanza del relativo oggetto SafeFileHandle.Note that the example does not properly instantiate its SafeFileHandle object.

Di seguito è illustrato il modello generale per implementare il modello Dispose per una classe di base che esegue l'override di 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

Suggerimento

In C# si crea un finalizzatore eseguendo l'override di Object.Finalize .In C#, you create a finalizer by overriding Object.Finalize. In Visual Basic, questa operazione viene eseguita con Protected Overrides Sub Finalize() .In Visual Basic, this is done with Protected Overrides Sub Finalize().

Implementare il modello Dispose per una classe derivataImplement the dispose pattern for a derived class

Una classe derivata da una classe che implementa l'interfaccia IDisposable non deve implementare IDisposable, poiché l'implementazione della classe di base di IDisposable.Dispose viene ereditata dalle classi derivate.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. Al contrario, per pulire una classe derivata, è necessario specificare quanto segue:Instead, to cleanup a derived class, you provide the following:

  • protected override void Dispose(bool)Metodo che esegue l'override del metodo della classe di base ed esegue l'effettiva pulizia della classe derivata.A protected override void Dispose(bool) method that overrides the base class method and performs the actual cleanup of the derived class. Questo metodo deve anche chiamare il base.Dispose(bool) MyBase.Dispose(bool) Metodo (in Visual Basic) della classe di base e passare il relativo stato di eliminazione per l'argomento.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.
  • Una classe derivata da SafeHandle che esegua il wrapping della risorsa non gestita (consigliato) o un override al metodo Object.Finalize.Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. La classe SafeHandle fornisce un finalizzatore, evitando la necessità di codificarne uno.The SafeHandle class provides a finalizer that frees you from having to code one. Se si fornisce un finalizzatore, questo deve chiamare l' Dispose(bool) Overload di con un disposing argomento di false .If you do provide a finalizer, it must call the Dispose(bool) overload with a disposing argument of false.

Di seguito è illustrato il modello generale per implementare il modello Dispose per una classe derivata che usa un handle sicuro: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

Nota

L'esempio precedente usa un oggetto SafeFileHandle per illustrato il criterio; sarebbe possibile usare invece qualsiasi oggetto derivato da SafeHandle.The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Si noti che l'esempio non crea correttamente un'istanza del relativo oggetto SafeFileHandle.Note that the example does not properly instantiate its SafeFileHandle object.

Di seguito è illustrato il modello generale per implementare il modello Dispose per una classe derivata che esegue l'override di 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

Implementare il modello Dispose con handle sicuriImplement the dispose pattern with safe handles

L'esempio seguente illustra il modello Dispose per una classe di base, DisposableStreamResource, che usa handle sicuri per incapsulare le risorse non gestite.The following example illustrates the dispose pattern for a base class, DisposableStreamResource, that uses a safe handle to encapsulate unmanaged resources. Viene definita una classe DisposableStreamResource che usa SafeFileHandle per eseguire il wrapping di un oggetto Stream che rappresenta un file aperto.It defines a DisposableStreamResource class that uses a SafeFileHandle to wrap a Stream object that represents an open file. La classe include anche una singola proprietà, Size , che restituisce il numero totale di byte nel flusso di file.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

Implementare il modello Dispose per una classe derivata con handle sicuriImplement the dispose pattern for a derived class with safe handles

Nell'esempio seguente viene illustrato il modello Dispose per una classe derivata, DisposableStreamResource2, che eredita dalla classe DisposableStreamResource presentata nell'esempio precedente.The following example illustrates the dispose pattern for a derived class, DisposableStreamResource2, that inherits from the DisposableStreamResource class presented in the previous example. La classe aggiunge un metodo aggiuntivo, WriteFileInfo, e usa un oggetto SafeFileHandle per il wrapping dell'handle del file modificabile.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

Vedere ancheSee also