Dispose 메서드는 주로 관리되지 않는 리소스를 해제하기 위해 구현됩니다. IDisposable 구현인 인스턴스 멤버를 사용하는 경우 일반적으로 Dispose 호출을 연계하는 것이 일반적입니다. Dispose구현하는 다른 이유가 있습니다. 예를 들어 할당된 메모리를 해제하거나, 컬렉션에 추가된 항목을 제거하거나, 획득한 잠금 해제를 신호로 표시합니다.
.NET 가비지 수집기 관리되지 않는 메모리를 할당하거나 해제하지 않습니다. 개체를 소멸시키는 패턴인 소멸 패턴은 개체의 수명에 질서를 부여합니다. 삭제 패턴은 IDisposable 인터페이스를 구현하는 개체에 사용됩니다. 이 패턴은 가비지 수집기가 관리되지 않는 개체를 회수할 수 없기 때문에 파일 및 파이프 핸들, 레지스트리 핸들, 대기 핸들 또는 관리되지 않는 메모리 블록에 대한 포인터와 상호 작용하는 경우에 일반적입니다.
리소스가 항상 적절하게 정리되도록 하기 위해서는, Dispose 메서드가 예외를 발생시키지 않고 여러 번 호출이 가능하도록 idempotent해야 합니다. 또한 후속 Dispose 호출은 아무 것도 수행하지 않아야 합니다.
GC.KeepAlive 메서드에 제공된 코드 예제는 개체 또는 해당 멤버에 대한 관리되지 않는 참조가 계속 사용 중인 동안 가비지 수집으로 인해 종료자가 실행되는 방법을 보여줍니다. 현재 루틴의 시작부터 이 메서드가 호출되는 지점까지 개체를 가비지 수집할 수 없도록 하려면 GC.KeepAlive 활용하는 것이 합리적일 수 있습니다.
팁
종속성 주입과 관련하여 IServiceCollection서비스에 대한 등록 시, 서비스 수명는 사용자를 대신하여 암시적으로 관리됩니다. IServiceProvider 및 해당 IHost 리소스 정리를 오케스트레이션합니다. 특히 IDisposable 및 IAsyncDisposable 구현은 지정된 수명이 끝날 때 올바르게 삭제됩니다.
자세한 내용은 .NET의 종속성 주입을 참조하세요.
계단식 처리 해제 호출
클래스가 IDisposable구현하는 다른 형식의 인스턴스를 소유하는 경우 포함하는 클래스 자체도 IDisposable구현해야 합니다. 일반적으로 IDisposable 구현을 인스턴스화하고 인스턴스 멤버(또는 속성)로 저장하는 클래스도 해당 정리를 담당합니다. 이렇게 하면 참조된 삭제 가능한 형식에 Dispose 메서드를 통해 결정적으로 정리를 수행할 수 있는 기회가 제공됩니다. 다음 예제에서 클래스는 sealed
(또는 Visual Basic에서는 NotInheritable
)입니다.
using System;
public sealed class Foo : IDisposable
{
private readonly IDisposable _bar;
public Foo()
{
_bar = new Bar();
}
public void Dispose() => _bar.Dispose();
}
Public NotInheritable Class Foo
Implements IDisposable
Private ReadOnly _bar As IDisposable
Public Sub New()
_bar = New Bar()
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
_bar.Dispose()
End Sub
End Class
팁
- 클래스에 IDisposable 필드 또는 속성이 있지만 소유하지 경우 클래스는 IDisposable구현할 필요가 없습니다. 일반적으로 IDisposable 자식 개체를 만들고 저장하는 클래스도 소유자가 되지만 경우에 따라 소유권을 다른 IDisposable 형식으로 이전할 수 있습니다.
- 종료자에서
null
-checking을 수행하려는 경우가 있습니다(이는 종료자에 의해 호출되는Dispose(false)
메서드를 포함합니다). 주된 이유 중 하나는 인스턴스가 완전히 초기화되었는지 확실하지 않은 경우입니다(예: 생성자에서 예외가 throw될 수 있습니다).
Dispose() 및 Dispose(bool)
IDisposable 인터페이스에는 매개 변수가 없는 단일 메서드 Dispose구현해야 합니다. 또한 봉인되지 않은 클래스에는 Dispose(bool)
오버로드 메서드가 있어야 합니다.
메서드 서명은 다음과 같습니다.
-
public
가상 구현이 아님(NotOverridable
은 Visual Basic,IDisposable.Dispose 구현). -
protected virtual
(Visual Basic에서는Overridable
)Dispose(bool)
.
Dispose() 메서드
Visual Basic에서는 가상이 아닌public
, NotOverridable
, 매개 변수가 없는 Dispose
메서드가 (형식의 소비자에 의해) 더 이상 필요하지 않을 때 호출됩니다. 이 메서드의 목적은 관리되지 않는 리소스를 해제하고, 일반 정리를 수행하며, 종료자가 있는 경우 종료자가 실행될 필요가 없음을 나타내는 것입니다. 관리되는 개체와 연결된 실제 메모리를 해제하는 것은 항상 가비지 수집기도메인입니다. 이 때문에 다음과 같은 표준 구현이 있습니다.
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 재정의를 호출할 필요가 없습니다. 따라서 SuppressFinalize 메서드를 호출하면 가비지 수집기가 종료자를 실행하지 않게 됩니다. 형식에 종료자가 없으면 GC.SuppressFinalize 호출이 적용되지 않습니다. 실제 정리는 Dispose(bool)
메서드 오버로드에 의해 수행됩니다.
Dispose(bool) 메서드 오버로드
오버로드에서 disposing
매개 변수는 메서드 호출이 Boolean 메서드(값이 Dispose) 또는 종료자(해당 값이 true
)에서 오는지 여부를 나타내는 false
.
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
// Dispose managed state (managed objects).
// ...
}
// Free unmanaged resources.
// ...
_disposed = true;
}
Protected Overridable Sub Dispose(disposing As Boolean)
If disposed Then Exit Sub
If disposing Then
' Free managed resources.
' ...
End If
' Free unmanaged resources.
' ...
disposed = True
End Sub
중요하다
종료자에서 호출될 때 disposing
매개 변수는 false
이어야 하고, true
메서드에서 호출될 때는 IDisposable.Dispose이어야 합니다. 즉, 결정적으로 호출될 때는 true
이고 비결정적으로 호출될 때는 false
입니다.
메서드 본문은 다음 세 가지 코드 블록으로 구성됩니다.
개체가 이미 삭제된 경우 조건부 반환을 위한 블록입니다.
관리되는 리소스를 해제하는 조건부 블록입니다. 이 블록은
disposing
값이true
경우 실행됩니다. 관리 해제되는 리소스에는 다음이 포함될 수 있습니다.- IDisposable을 구현하는 관리 객체입니다. 조건부 블록을 사용하여 Dispose 구현(cascade dispose)을 호출할 수 있습니다. 파생 클래스의 System.Runtime.InteropServices.SafeHandle 사용하여 관리되지 않는 리소스를 래핑한 경우 여기서 SafeHandle.Dispose() 구현을 호출해야 합니다.
- 많은 양의 메모리를 사용하거나 부족한 리소스를 사용하는 관리되는 개체입니다. 대용량 관리되는 개체 참조를
null
에 할당하여 더욱 연결할 수 없도록 합니다. 이렇게 하면 비결정적으로 회수된 경우보다 더 빠르게 릴리스됩니다.
관리되지 않는 리소스를 해제하는 블록입니다. 이 블록은
disposing
매개 변수의 값에 관계없이 실행됩니다.
메서드 호출이 종료자에서 오는 경우 관리되지 않는 리소스를 해제하는 코드만 실행해야 합니다. 구현자는 잘못된 경로가 삭제되었을 수 있는 관리되는 개체와 상호 작용하지 않도록 해야 합니다. 이는 종료 중에 가비지 수집기가 관리되는 개체를 삭제하는 순서가 비결정적이므로 중요합니다.
삭제 패턴 구현
봉인되지 않은 모든 클래스(또는 NotInheritable
수정되지 않은 Visual Basic 클래스)는 상속될 수 있으므로 잠재적인 기본 클래스로 간주되어야 합니다. 잠재적인 기본 클래스에 대한 삭제 패턴을 구현하는 경우 클래스에 다음 메서드를 추가해야 합니다.
-
Dispose 메서드를 호출하는
Dispose(bool)
구현입니다. - 실제 정리를 수행하는
Dispose(bool)
메서드입니다. - 클래스가 관리되지 않는 리소스를 다룬다면 관리되지 않는 리소스를 SafeHandle으로 래핑하거나 Object.Finalize 메서드를 재정의하여 제공하십시오.
중요하다
관리되지 않는 리소스를 직접 참조하는 경우에만 종료자(예: Object.Finalize 재정의)가 필요합니다. 일반적으로 방지할 수 있는 고급 시나리오입니다.
- 클래스가관리되는 개체만 참조하는 경우에도 클래스에서 삭제 패턴을 구현할 수 있습니다. 종료자를 구현할 필요가 없습니다.
- 관리되지 않는 리소스처리해야 하는 경우, 관리되지 않는 IntPtr 핸들을 SafeHandle에 감싸는 것을 강력히 권장합니다. SafeHandle 종료자를 제공하므로 직접 작성할 필요가 없습니다. 자세한 내용은 Safe 핸들 단락을 참조하세요.
관리되는 리소스가 있는 기본 클래스
다음은 관리되는 리소스만 소유하는 기본 클래스에 대한 삭제 패턴을 구현하는 일반적인 예입니다.
using System;
using System.IO;
public class DisposableBase : IDisposable
{
// Detect redundant Dispose() calls.
private bool _isDisposed;
// Instantiate a disposable object owned by this class.
private Stream? _managedResource = new MemoryStream();
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
// Dispose managed state.
_managedResource?.Dispose();
_managedResource = null;
}
}
}
}
Imports System.IO
Public Class DisposableBase
Implements IDisposable
' Detect redundant Dispose() calls.
Private _isDisposed As Boolean
' Instantiate a disposable object owned by this class.
Private _managedResource As Stream = New MemoryStream()
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
If Not _isDisposed Then
_isDisposed = True
If disposing Then
' Dispose managed state.
_managedResource?.Dispose()
_managedResource = Nothing
End If
End If
End Sub
End Class
메모
이전 예제에서는 더미MemoryStream 개체를 사용하여 패턴을 보여 줍니다. 대신 어떤 IDisposable라도 사용할 수 있습니다.
관리되지 않는 리소스가 있는 기본 클래스
다음은 소유하는 관리되지 않는 리소스를 정리하기 위해 Object.Finalize 재정의하는 기본 클래스에 대한 삭제 패턴을 구현하는 예제입니다. 이 예제에서는 스레드로부터 안전한 방식으로 Dispose(bool)
구현하는 방법도 보여 줍니다. 다중 스레드 애플리케이션에서 관리되지 않는 리소스를 처리할 때 동기화가 중요할 수 있습니다. 앞에서 설명한 것처럼 이 시나리오는 고급 시나리오입니다.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
public class DisposableBaseWithFinalizer : IDisposable
{
// Detect redundant Dispose() calls in a thread-safe manner.
// _isDisposed == 0 means Dispose(bool) has not been called yet.
// _isDisposed == 1 means Dispose(bool) has been already called.
private int _isDisposed;
// Instantiate a disposable object owned by this class.
private Stream? _managedResource = new MemoryStream();
// A pointer to 10 bytes allocated on the unmanaged heap.
private IntPtr _unmanagedResource = Marshal.AllocHGlobal(10);
~DisposableBaseWithFinalizer() => Dispose(false);
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
// In case _isDisposed is 0, atomically set it to 1.
// Enter the branch only if the original value is 0.
if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
{
if (disposing)
{
_managedResource?.Dispose();
_managedResource = null;
}
Marshal.FreeHGlobal(_unmanagedResource);
}
}
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Threading
Public Class DisposableBaseWithFinalizer
Implements IDisposable
' Detect redundant Dispose() calls in a thread-safe manner.
' _isDisposed == 0 means Dispose(bool) has not been called yet.
' _isDisposed == 1 means Dispose(bool) has been already called.
Private _isDisposed As Integer
' Instantiate a disposable object owned by this class.
Private _managedResource As Stream = New MemoryStream()
' A pointer to 10 bytes allocated on the unmanaged heap.
Private _unmanagedResource As IntPtr = Marshal.AllocHGlobal(10)
Protected Overrides Sub Finalize()
Dispose(False)
End Sub
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
' In case _isDisposed is 0, atomically set it to 1.
' Enter the branch only if the original value is 0.
If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 Then
If disposing Then
_managedResource?.Dispose()
_managedResource = Nothing
End If
Marshal.FreeHGlobal(_unmanagedResource)
End If
End Sub
End Class
메모
- 이전 예제에서는 AllocHGlobal 사용하여 생성자의 관리되지 않는 힙에 10바이트를 할당하고 FreeHGlobal호출하여
Dispose(bool)
버퍼를 해제합니다. 이는 예시를 위한 임시 할당입니다. - 다시 말하지만 종료자를 구현하지 않는 것이 좋습니다. 사용자 지정 안전 핸들을 사용하여 비결정적 종료 및 동기화를 SafeHandle에 위임하는 이전 예제와 동등한 삭제 패턴 구현을 참조하세요.
팁
C#에서는 종료자를 제공하여 종료를 구현하며, Object.Finalize을 재정의하지 않습니다. Visual Basic에서는 Protected Overrides Sub Finalize()
사용하여 종료자를 만듭니다.
파생 클래스에 대한 삭제 패턴 구현
IDisposable 인터페이스를 구현하는 클래스에서 파생된 클래스는 IDisposable 기본 클래스 구현이 파생 클래스에 의해 상속되므로 IDisposable.Dispose구현하면 안 됩니다. 대신 파생 클래스를 정리하려면 다음을 제공합니다.
- 기본 클래스 메서드를 재정의하고 파생 클래스의 실제 정리를 수행하는
protected override void Dispose(bool)
메서드입니다. 또한 이 메서드는 삭제 상태(base.Dispose(bool)
매개 변수)를 인수로 전달하는MyBase.Dispose(bool)
(Visual Basic의bool disposing
) 메서드를 호출해야 합니다. - 관리되지 않는 리소스를 감싸는 SafeHandle의 파생 클래스(권장) 또는 Object.Finalize 메서드를 재정의하는 방법 중 하나입니다.
SafeHandle 클래스는 종료자를 제공하여 직접 종료자를 코딩할 필요가 없게 해 줍니다. 종료자를 제공하는 경우에는
Dispose(bool)
오버로드를false
인수를 사용하여 호출해야 합니다.
다음은 안전 핸들을 사용하는 파생 클래스에 대한 삭제 패턴을 구현하기 위한 일반적인 패턴의 예입니다.
using System.IO;
public class DisposableDerived : DisposableBase
{
// To detect redundant calls
private bool _isDisposed;
// Instantiate a disposable object owned by this class.
private Stream? _managedResource = new MemoryStream();
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
_managedResource?.Dispose();
_managedResource = null;
}
}
// Call base class implementation.
base.Dispose(disposing);
}
}
Imports System.IO
Public Class DisposableDerived
Inherits DisposableBase
' To detect redundant calls
Private _isDisposed As Boolean
' Instantiate a disposable object owned by this class.
Private _managedResource As Stream = New MemoryStream()
' Protected implementation of Dispose pattern.
Protected Overrides Sub Dispose(disposing As Boolean)
If Not _isDisposed Then
_isDisposed = True
If disposing Then
_managedResource?.Dispose()
_managedResource = Nothing
End If
End If
' Call base class implementation.
MyBase.Dispose(disposing)
End Sub
End Class
메모
이전 예제에서는 SafeFileHandle 개체를 사용하여 패턴을 보여 줍니다. SafeHandle 파생된 모든 개체를 대신 사용할 수 있습니다. 이 예제에서는 SafeFileHandle 개체를 제대로 인스턴스화하지 않습니다.
다음은 Object.Finalize재정의하는 파생 클래스에 대한 dispose 패턴을 구현하는 일반적인 패턴입니다.
using System.Threading;
public class DisposableDerivedWithFinalizer : DisposableBaseWithFinalizer
{
// Detect redundant Dispose() calls in a thread-safe manner.
// _isDisposed == 0 means Dispose(bool) has not been called yet.
// _isDisposed == 1 means Dispose(bool) has been already called.
private int _isDisposed;
~DisposableDerivedWithFinalizer() => Dispose(false);
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
// In case _isDisposed is 0, atomically set it to 1.
// Enter the branch only if the original value is 0.
if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
}
// Call the base class implementation.
base.Dispose(disposing);
}
}
Imports System.Threading
Public Class DisposableDerivedWithFinalizer
Inherits DisposableBaseWithFinalizer
' Detect redundant Dispose() calls in a thread-safe manner.
' _isDisposed == 0 means Dispose(bool) has not been called yet.
' _isDisposed == 1 means Dispose(bool) has been already called.
Private _isDisposed As Integer
Protected Overrides Sub Finalize()
Dispose(False)
End Sub
' Protected implementation of Dispose pattern.
Protected Overrides Sub Dispose(disposing As Boolean)
' In case _isDisposed is 0, atomically set it to 1.
' Enter the branch only if the original value is 0.
If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 Then
If disposing Then
' TODO: dispose managed state (managed objects).
End If
' TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
' TODO: set large fields to null.
End If
' Call the base class implementation.
MyBase.Dispose(disposing)
End Sub
End Class
안전한 핸들
객체의 파이널라이저 코드를 작성하는 것은 복잡한 작업이며, 올바르게 수행되지 않으면 문제를 일으킬 수 있습니다. 따라서 종료자를 구현하는 대신 System.Runtime.InteropServices.SafeHandle 개체를 생성하는 것이 좋습니다.
System.Runtime.InteropServices.SafeHandle는 관리되지 않는 리소스를 식별하는 System.IntPtr을 래핑하는 추상적인 관리 형식입니다. Windows에서는 핸들을 식별하고 Unix에서는 파일 설명자를 식별할 수 있습니다.
SafeHandle
SafeHandle
삭제되거나 SafeHandle
대한 모든 참조가 삭제되고 SafeHandle
인스턴스가 종료될 때 이 리소스가 한 번만 해제되도록 하는 데 필요한 모든 논리를 제공합니다.
System.Runtime.InteropServices.SafeHandle 추상 기본 클래스입니다. 파생 클래스는 다양한 종류의 핸들에 대한 특정 인스턴스를 제공합니다. 이러한 파생 클래스는 잘못된 것으로 간주되는 System.IntPtr 값과 핸들을 실제로 해제하는 방법을 확인합니다. 예를 들어 SafeFileHandle는 SafeHandle
에서 파생되어, 열린 파일 핸들과 설명자를 식별하는 IntPtrs
를 래핑하며, Unix에서는 close
함수, Windows에서는 CloseHandle
함수를 통해 이를 닫기 위해 SafeHandle.ReleaseHandle() 메서드를 재정의합니다. .NET 라이브러리의 대부분의 API는 관리되지 않는 리소스를 생성할 때 원시 포인터를 직접 반환하는 대신, 그 리소스를 SafeHandle
로 감싸고, 필요에 따라 SafeHandle
로 반환합니다. 관리되지 않는 구성 요소와 상호 작용하고 관리되지 않는 리소스에 대한 IntPtr
를 받는 경우, 이를 래핑할 수 있는 고유한 SafeHandle
형식을 만들 수 있습니다. 따라서SafeHandle
형식이 아닌 형식 중에서 종료자를 구현해야 하는 경우는 거의 없습니다. 대부분의 삭제 가능한 패턴 구현은 다른 관리되는 리소스만 래핑하며, 그 중 일부는 SafeHandle
개체일 수 있습니다.
사용자 지정 안전 핸들을 사용하여 삭제 패턴 구현
다음 코드에서는 SafeHandle구현하여 관리되지 않는 리소스를 처리하는 방법을 보여 줍니다.
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
// Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
class LocalAllocHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private LocalAllocHandle() : base(ownsHandle: true) { }
// No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle);
return true;
}
// Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
public static LocalAllocHandle Allocate(int numberOfBytes)
{
IntPtr nativeHandle = Marshal.AllocHGlobal(numberOfBytes);
LocalAllocHandle safeHandle = new LocalAllocHandle();
safeHandle.SetHandle(nativeHandle);
return safeHandle;
}
}
public class DisposableBaseWithSafeHandle : IDisposable
{
// Detect redundant Dispose() calls.
private bool _isDisposed;
// Managed disposable objects owned by this class
private LocalAllocHandle? _safeHandle = LocalAllocHandle.Allocate(10);
private Stream? _otherUnmanagedResource = new MemoryStream();
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
// Dispose managed state.
_otherUnmanagedResource?.Dispose();
_safeHandle?.Dispose();
_otherUnmanagedResource = null;
_safeHandle = null;
}
}
}
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports Microsoft.Win32.SafeHandles
' Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
Public Class LocalAllocHandle
Inherits SafeHandleZeroOrMinusOneIsInvalid
Private Sub New()
MyBase.New(True)
End Sub
' No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
Protected Overrides Function ReleaseHandle() As Boolean
Marshal.FreeHGlobal(handle)
Return True
End Function
' Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
Public Shared Function Allocate(numberOfBytes As Integer) As LocalAllocHandle
Dim nativeHandle As IntPtr = Marshal.AllocHGlobal(numberOfBytes)
Dim safeHandle As New LocalAllocHandle()
safeHandle.SetHandle(nativeHandle)
Return safeHandle
End Function
End Class
Public Class DisposableBaseWithSafeHandle
Implements IDisposable
' Detect redundant Dispose() calls.
Private _isDisposed As Boolean
' Managed disposable objects owned by this class
Private _safeHandle As LocalAllocHandle = LocalAllocHandle.Allocate(10)
Private _otherUnmanagedResource As Stream = New MemoryStream()
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
If Not _isDisposed Then
_isDisposed = True
If disposing Then
' Dispose managed state.
_otherUnmanagedResource?.Dispose()
_safeHandle?.Dispose()
_otherUnmanagedResource = Nothing
_safeHandle = Nothing
End If
End If
End Sub
End Class
메모
DisposableBaseWithSafeHandle
클래스의 동작은 이전 예제 DisposableBaseWithFinalizer
클래스의 동작과 동일하지만 여기서 보여 준 방법은 더 안전합니다.
- 종료는 SafeHandle이 처리하므로, 종료자를 구현할 필요가 없습니다.
- 스레드 안전을 보장하기 위해 동기화할 필요가 없습니다.
DisposableBaseWithSafeHandle
의Dispose
구현에 경합 조건이 있더라도, SafeHandle는 SafeHandle.ReleaseHandle이 한 번만 호출되도록 보장합니다.
.NET의 기본 제공 안전 핸들
Microsoft.Win32.SafeHandles 네임스페이스의 다음 파생 클래스는 안전한 핸들을 제공합니다.
수업 | 보유하는 리소스 |
---|---|
SafeFileHandle SafeMemoryMappedFileHandle SafePipeHandle |
파일, 메모리 매핑된 파일 및 파이프 |
SafeMemoryMappedViewHandle | 메모리 보기 |
SafeNCryptKeyHandle SafeNCryptProviderHandle SafeNCryptSecretHandle |
암호화 구문 |
SafeRegistryHandle | 레지스트리 키 |
SafeWaitHandle | 대기 핸들 |
또한 참조하십시오
.NET