Dispose 메서드 구현Implementing a Dispose method

애플리케이션에서 사용되는 관리되지 않는 리소스를 해제하려면 Dispose 메서드를 구현합니다.You implement a Dispose method to release unmanaged resources used by your application. .NET 가비지 수집기는 관리되지 않는 메모리를 할당하거나 해제하지 않습니다.The .NET garbage collector does not allocate or release unmanaged memory.

삭제 패턴이라고도 하는 개체 삭제 패턴에서는 개체의 수명에 순서를 적용합니다.The pattern for disposing an object, referred to as a dispose pattern, imposes order on the lifetime of an object. 삭제 패턴은 파일 핸들, 파이프 핸들, 레지스트리 핸들, 대기 핸들 또는 관리되지 않는 메모리의 블록에 대한 포인터와 같이 관리되지 않는 리소스에 액세스하는 개체에만 사용됩니다.The dispose pattern is used only for objects that access unmanaged resources, such as file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. 이는 가비지 수집기가 사용되지 않은 관리되는 개체를 회수하는 데 매우 효율적이지만, 관리되지 않는 개체는 회수할 수 없기 때문입니다.This is because the garbage collector is very efficient at reclaiming unused managed objects, but it is unable to reclaim unmanaged objects.

삭제 패턴에는 두 가지 변형이 있습니다.The dispose pattern has two variations:

리소스가 항상 적절하게 정리되게 하려면 예외를 throw하지 않고 Dispose 메서드를 여러 번 호출해야 합니다.To help ensure that resources are always cleaned up appropriately, a Dispose method should be callable multiple times without throwing an exception.

GC.KeepAlive 메서드의 코드 예제에서는 적극적인 가비지 수집으로 인해, 회수된 개체의 멤버가 아직 실행되는 동안 종료자가 실행되게 할 수 있는 방법을 보여 줍니다.The code example provided for the GC.KeepAlive method shows how aggressive garbage collection can cause a finalizer to run while a member of the reclaimed object is still executing. KeepAlive 메서드를 오래 실행한 후에는 Dispose 메서드를 호출하는 것이 좋습니다.It is a good idea to call the KeepAlive method at the end of a lengthy Dispose method.

Dispose() 및 Dispose(Boolean)Dispose() and Dispose(Boolean)

IDisposable 인터페이스에서는 매개 변수가 없는 단일 메서드인 Dispose를 구현해야 합니다.The IDisposable interface requires the implementation of a single parameterless method, Dispose. 그러나 삭제 패턴을 사용하려면 다음과 같은 두 가지 Dispose 메서드를 구현해야 합니다.However, the dispose pattern requires two Dispose methods to be implemented:

  • 매개 변수가 없는 공용 비가상(Visual Basic의 NonInheritable) IDisposable.Dispose 구현A public non-virtual (NonInheritable in Visual Basic) IDisposable.Dispose implementation that has no parameters.

  • 시그니처가 다음과 같은 보호된 가상(Visual Basic의 Overridable) Dispose 메서드: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)
    

Dispose() 오버로드The Dispose() overload

공용, 비가상(Visual Basic의 경우 NonInheritable), 매개 변수가 없는 Dispose 메서드는 형식의 소비자에 의해 호출되므로, 관리되지 않는 리소스를 해제하고 종료자(있는 경우)를 실행할 필요가 없음을 나타내기 위한 것입니다.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 and to indicate that the finalizer, if one is present, doesn't have to run. 이로 인해 다음과 같은 표준 구현이 수행됩니다.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 메서드가 모든 개체를 정리하므로, 가비지 수집기가 더 이상 개체의 Object.Finalize 재정의를 호출할 필요가 없습니다.The Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects' Object.Finalize override. 따라서 SuppressFinalize 메서드를 호출하면 가비지 수집기가 종료자를 실행하지 않도록 방지됩니다.Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. 형식에 종료자가 없는 경우 GC.SuppressFinalize에 대한 호출이 영향을 미치지 않습니다.If the type has no finalizer, the call to GC.SuppressFinalize has no effect. Dispose 메서드의 두 번째 오버로드에서 실제로 관리되지 않는 리소스를 해제하는 작업이 수행됩니다.Note that the actual work of releasing unmanaged resources is performed by the second overload of the Dispose method.

Dispose(Boolean) 오버로드The Dispose(Boolean) overload

두 번째 오버로드에서 disposing 매개 변수는 메서드 호출이 Dispose 메서드(값은 true)에서 수행되는지 또는 종료자(값은 false)에서 수행되는지를 나타내는 Boolean입니다.In the second 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).

메서드 본문은 다음과 같은 두 가지 코드 블록으로 구성됩니다.The body of the method consists of two blocks of code:

  • 관리되지 않는 리소스를 해제하는 블록.A block that frees unmanaged resources. 이 블록은 disposing 매개 변수의 값에 관계없이 실행됩니다.This block executes regardless of the value of the disposing parameter.

  • 관리되는 리소스를 해제하는 조건부 블록.A conditional block that frees managed resources. 이 블록은 disposing 값이 true인 경우 실행됩니다.This block executes if the value of disposing is true. 해제되는 관리되는 리소스는 다음과 같습니다.The managed resources that it frees can include:

    IDisposable을 구현하는 관리되는 개체.Managed objects that implement IDisposable. 조건부 블록을 사용하여 Dispose 구현을 호출할 수 있습니다.The conditional block can be used to call their Dispose implementation. 관리되지 않는 리소스를 래핑하기 위해 SafeHandle을 사용한 경우 여기에서 SafeHandle.Dispose(Boolean) 구현을 호출해야 합니다.If you have used a safe handle to wrap your unmanaged resource, you should call the SafeHandle.Dispose(Boolean) implementation here.

    많은 메모리를 사용하거나 부족한 리소스를 사용하는 관리되는 개체.Managed objects that consume large amounts of memory or consume scarce resources. 이러한 개체를 Dispose 메서드에서 명시적으로 해제하면 가비지 수집기에서 명확하지 않게 회수한 경우보다 빠르게 해제할 수 있습니다.Freeing these objects explicitly in the Dispose method releases them faster than if they were reclaimed non-deterministically by the garbage collector.

메서드 호출이 종료자에서 나오는 경우, 즉 disposingfalse인 경우 관리되지 않는 리소스를 해제하는 코드만 실행됩니다.If the method call comes from a finalizer (that is, if disposing is false), only the code that frees unmanaged resources executes. 종료하는 동안 가비지 수집기가 관리되는 개체를 제거하는 순서가 정의되어 있지 않기 때문에 Dispose 값으로 이 false 오버로드를 호출하면 종료자가 이미 회수된 관리되는 리소스를 해제하려고 시도하지 못합니다.Because the order in which the garbage collector destroys managed objects during finalization is not defined, calling this Dispose overload with a value of false prevents the finalizer from trying to release managed resources that may have already been reclaimed.

기본 클래스에 대한 삭제 패턴 구현Implementing the dispose pattern for a base class

기본 클래스에 대한 삭제 패턴을 구현하는 경우 다음을 제공해야 합니다.If you implement the dispose pattern for a base class, you must provide the following:

중요

Dispose()을 구현하고 sealed(Visual Basic의 경우 NotInheritable)가 아닌 모든 기본 클래스에 대해 이 패턴을 구현해야 합니다.You should implement this pattern for all base classes that implement Dispose() and are not sealed (NotInheritable in Visual Basic).

  • Dispose 메서드를 호출하는 Dispose(Boolean) 구현A Dispose implementation that calls the Dispose(Boolean) method.

  • 실제로 리소스를 해제하는 작업을 수행하는 Dispose(Boolean) 메서드A Dispose(Boolean) method that performs the actual work of releasing resources.

  • 관리되지 않는 리소스를 래핑하는 SafeHandle에서 파생된 클래스(권장) 또는 Object.Finalize 메서드 재정의Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. SafeHandle 클래스는 사용자가 코딩할 필요가 없는 종료자를 제공합니다.The SafeHandle class provides a finalizer that frees you from having to code one.

SafeHandle을 사용하는 기본 클래스에 대한 삭제 패턴을 구현하는 일반적인 패턴은 다음과 같습니다.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
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   // Instantiate a SafeHandle instance.
   SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
   
   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }
   
   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         handle.Dispose();
         // Free any other managed objects here.
         //
      }
      
      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

참고

이전 예제에서는 SafeFileHandle 개체를 사용하여 패턴을 보여 줍니다. SafeHandle에서 파생된 개체를 대신 사용할 수 있습니다.The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. 예제에서는 해당 SafeFileHandle 개체를 제대로 인스턴스화하지 않습니다.Note that the example does not properly instantiate its SafeFileHandle object.

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
{
   // Flag: Has Dispose already been called?
   bool disposed = 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) {
         // Free any other managed objects here.
         //
      }
      
      // Free any unmanaged objects here.
      //
      disposed = true;
   }

   ~BaseClass()
   {
      Dispose(false);
   }
}
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

참고

C#에서 소멸자를 정의하여 Object.Finalize를 재정의합니다.In C#, you override Object.Finalize by defining a destructor.

파생된 클래스에 대한 삭제 패턴 구현Implementing the dispose pattern for a derived class

IDisposable의 기본 클래스 구현은 파생된 클래스에 의해 상속되므로 IDisposable 인터페이스를 구현하는 클래스에서 파생된 클래스가 IDisposable.Dispose을 구현하지 않아야 합니다.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. 대신 파생된 클래스에 대한 삭제 패턴을 구현하려면 다음을 제공합니다.Instead, to implement the dispose pattern for a derived class, you provide the following:

  • 기본 클래스 메서드를 재정의하고 파생된 클래스의 리소스를 해제하는 실제 작업을 수행하는 protected Dispose(Boolean) 메서드.A protected Dispose(Boolean) method that overrides the base class method and performs the actual work of releasing the resources of the derived class. 또한 이 메서드는 기본 클래스의 Dispose(Boolean) 메서드를 호출하며 인수에 대해 삭제 중 상태를 전달합니다.This method should also call the Dispose(Boolean) method of the base class and pass its disposing status for the argument.

  • 관리되지 않는 리소스를 래핑하는 SafeHandle에서 파생된 클래스(권장) 또는 Object.Finalize 메서드 재정의Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. SafeHandle 클래스는 사용자가 코딩할 필요가 없는 종료자를 제공합니다.The SafeHandle class provides a finalizer that frees you from having to code one. 종료자를 제공하는 경우 disposing 인수가 falseDispose(Boolean) 오버로드를 호출해야 합니다.If you do provide a finalizer, it should call the Dispose(Boolean) overload with a disposing argument of false.

SafeHandle을 사용하는 파생된 클래스에 대한 삭제 패턴을 구현하는 일반적인 패턴은 다음과 같습니다.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
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   // Instantiate a SafeHandle instance.
   SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);

   // Protected implementation of Dispose pattern.
   protected override void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         handle.Dispose();
         // Free any other managed objects here.
         //
      }
      
      // Free any unmanaged objects here.
      //

      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

참고

이전 예제에서는 SafeFileHandle 개체를 사용하여 패턴을 보여 줍니다. SafeHandle에서 파생된 개체를 대신 사용할 수 있습니다.The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. 예제에서는 해당 SafeFileHandle 개체를 제대로 인스턴스화하지 않습니다.Note that the example does not properly instantiate its SafeFileHandle object.

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
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   
   // Protected implementation of Dispose pattern.
   protected override void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         // Free any other managed objects here.
         //
      }
      
      // Free any unmanaged objects here.
      //
      disposed = true;
      
      // Call the base class implementation.
      base.Dispose(disposing);
   }

   ~DerivedClass()
   {
      Dispose(false);
   }
}
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

참고

C#에서 소멸자를 정의하여 Object.Finalize를 재정의합니다.In C#, you override Object.Finalize by defining a destructor.

SafeHandle 사용Using safe handles

개체 종료자에 대한 코드를 작성하는 작업은 올바르게 수행되지 않을 경우 문제를 일으킬 수 있는 복잡한 작업입니다.Writing code for an object's finalizer is a complex task that can cause problems if not done correctly. 따라서 종료자를 구현하는 대신 System.Runtime.InteropServices.SafeHandle 개체를 생성하는 것이 좋습니다.Therefore, we recommend that you construct System.Runtime.InteropServices.SafeHandle objects instead of implementing a finalizer.

System.Runtime.InteropServices.SafeHandle 클래스에서 파생된 클래스는 중단 없이 핸들을 할당 및 해제하여 개체 수명 문제를 단순화합니다.Classes derived from the System.Runtime.InteropServices.SafeHandle class simplify object lifetime issues by assigning and releasing handles without interruption. 여기에는 애플리케이션 도메인을 언로드하는 동안 실행이 보장되는 중요한 종료자가 포함됩니다.They contain a critical finalizer that is guaranteed to run while an application domain is unloading. SafeHandle을 사용하는 이점에 대한 자세한 내용은 System.Runtime.InteropServices.SafeHandle를 참조하세요.For more information about the advantages of using a safe handle, see System.Runtime.InteropServices.SafeHandle. Microsoft.Win32.SafeHandles 네임스페이스에서 다음과 같은 파생된 클래스가 SafeHandle을 제공합니다.The following derived classes in the Microsoft.Win32.SafeHandles namespace provide safe handles:

SafeHandle을 사용하여 기본 클래스에 대한 삭제 패턴 구현Using a safe handle to implement the dispose pattern for a base class

다음 예제는 SafeHandle을 사용하여 관리되지 않는 리소스를 캡슐화하는 기본 클래스에 대한 삭제 패턴인 DisposableStreamResource를 보여 줍니다.The following example illustrates the dispose pattern for a base class, DisposableStreamResource, that uses a safe handle to encapsulate unmanaged resources. 이는 DisposableResource을 사용하여 열려 있는 파일을 나타내는 SafeFileHandle 개체를 래핑하는 Stream 클래스를 정의합니다.It defines a DisposableResource class that uses a SafeFileHandle to wrap a Stream object that represents an open file. 또한 DisposableResource 메서드에는 파일 스트림에서 총 바이트 수를 반환하는 단일 속성인 Size도 포함됩니다.The DisposableResource method 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.IO;
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;
   protected IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
   private const int INVALID_FILE_SIZE = unchecked((int) 0xFFFFFFFF);
   
   // Define Windows APIs.
   [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode)]
   protected static extern IntPtr 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 SafeFileHandle safeHandle; 
   private long bufferSize;
   private int upperWord;
   
   public DisposableStreamResource(string filename)
   {
      if (filename == null)
         throw new ArgumentNullException("The filename cannot be null.");
      else if (filename == "")
         throw new ArgumentException("The filename cannot be an empty string.");
            
      IntPtr handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
                                 IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                                 IntPtr.Zero);
      if (handle != INVALID_HANDLE_VALUE)
         safeHandle = new SafeFileHandle(handle, true);
      else
         throw new FileNotFoundException(String.Format("Cannot open '{0}'", filename));
      
      // Get file size.
      bufferSize = GetFileSize(safeHandle, out upperWord); 
      if (bufferSize == INVALID_FILE_SIZE)
         bufferSize = -1;
      else if (upperWord > 0) 
         bufferSize = (((long)upperWord) << 32) + bufferSize;
   }
   
   public long Size 
   { get { return bufferSize; } }

   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 = &H0000000i
   Protected Const OPEN_EXISTING As UInteger = 3
   Protected Const FILE_ATTRIBUTE_NORMAL As UInteger = &H80
   Protected INVALID_HANDLE_VALUE As New IntPtr(-1)
   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 IntPtr
   Private Declare Function GetFileSize Lib "kernel32" (hFile As SafeFileHandle, 
                                                        ByRef lpFileSizeHigh As Integer) As Integer
    
   ' Define locals.
   Private disposed As Boolean = False
   Private safeHandle As SafeFileHandle 
   Private bufferSize As Long 
   Private upperWord As Integer
   
   Public Sub New(filename As String)
      If filename Is Nothing Then
         Throw New ArgumentNullException("The filename cannot be null.")
      Else If filename = ""
         Throw New ArgumentException("The filename cannot be an empty string.")
      End If
            
      Dim handle As IntPtr = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
                                        IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                                        IntPtr.Zero)
      If handle <> INVALID_HANDLE_VALUE Then
         safeHandle = New SafeFileHandle(handle, True)
      Else
         Throw New FileNotFoundException(String.Format("Cannot open '{0}'", filename))
      End If
      
      ' Get file size.
      bufferSize = GetFileSize(safeHandle, upperWord) 
      If bufferSize = INVALID_FILE_SIZE Then
         bufferSize = -1
      Else If upperWord > 0 Then 
         bufferSize = (CLng(upperWord) << 32) + bufferSize
      End If     
   End Sub
   
   Public ReadOnly Property Size As Long
      Get
         Return bufferSize
      End Get
   End Property
   
   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

SafeHandle을 사용하여 파생된 클래스에 대한 삭제 패턴 구현Using a safe handle to implement the dispose pattern for a derived class

다음 예제는 이전 예제에 제공된 DisposableStreamResource2 클래스에서 상속되는 파생된 클래스에 대한 삭제 패턴인 DisposableStreamResource를 보여 줍니다.The following example illustrates the dispose pattern for a derived class, DisposableStreamResource2, that inherits from the DisposableStreamResource class presented in the previous example. 클래스에서 추가 메서드인 WriteFileInfo를 추가하고 SafeFileHandle 개체를 사용하여 쓰기 가능 파일의 핸들을 래핑합니다.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.IO;
using System.Runtime.InteropServices;
using System.Threading;

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);
   
   // Define locals.
   private bool disposed = false;
   private string filename;
   private bool created = false;
   private SafeFileHandle safeHandle;
   
   public DisposableStreamResource2(string filename) : base(filename)
   {
      this.filename = filename;
   }
   
   public void WriteFileInfo()
   { 
      if (! created) {
         IntPtr hFile = CreateFile(@".\FileInfo.txt", GENERIC_WRITE, 0, 
                                   IntPtr.Zero, OPEN_ALWAYS, 
                                   FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
         if (hFile != INVALID_HANDLE_VALUE)
            safeHandle = new SafeFileHandle(hFile, true);
         else
            throw new IOException("Unable to create output file.");

         created = true;
      }

      string output = String.Format("{0}: {1:N0} bytes\n", filename, Size);
      int bytesWritten;
      bool result = WriteFile(safeHandle, output, output.Length, out bytesWritten, IntPtr.Zero);                                     
   }

   protected override void Dispose(bool disposing)
   {
      if (disposed) return;
      
      // Release any managed resources here.
      if (disposing)
         safeHandle.Dispose();
      
      disposed = true;
      
      // Release any unmanaged resources not wrapped by safe handles here.
      
      // 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 filename As String
   Private created As Boolean = False
   Private safeHandle As SafeFileHandle
   
   Public Sub New(filename As String)
      MyBase.New(filename)
      Me.filename = filename
   End Sub
   
   Public Sub WriteFileInfo() 
      If Not created Then
         Dim hFile As IntPtr = CreateFile(".\FileInfo.txt", GENERIC_WRITE, 0, 
                                          IntPtr.Zero, OPEN_ALWAYS, 
                                          FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)
         If hFile <> INVALID_HANDLE_VALUE Then
            safeHandle = New SafeFileHandle(hFile, True)
         Else
            Throw New IOException("Unable to create output file.")
         End If
         created = True
      End If
      Dim output As String = String.Format("{0}: {1:N0} bytes {2}", filename, Size, 
                                           vbCrLf)
      WriteFile(safeHandle, output, output.Length, 0&, Nothing)                                     
   End Sub

   Protected Overloads Overridable 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

참고 항목See also