Finalize 및 Dispose를 구현하여 관리되지 않는 리소스 정리

참고참고

마무리 및 c + +를 사용 하 여 리소스를 삭제 하는 방법에 대 한 자세한 참조 Destructors and Finalizers in Visual C++.

클래스 인스턴스는 종종 런타임에서 관리되지 않는 창 핸들(HWND), 데이터베이스 연결 등의 리소스에 대한 제어를 캡슐화합니다. 따라서 이러한 리소스를 해제하는 명시적 방법과 암시적 방법을 모두 제공해야 합니다. 개체에 대해 보호된 Finalize(C# 및 C++의 소멸자 구문)를 구현하여 암시적 제어 수단을 제공합니다. 가비지 수집기는 해당 개체에 대해 유효한 참조가 없어진 후 일정 시점에 이 메서드를 호출합니다.

일부 경우에는 개체를 사용하는 프로그래머에게 가비지 수집기가 해당 개체를 해제하기 전에 이러한 외부 리소스를 명시적으로 해제할 수 있는 기능을 제공할 수도 있습니다. 외부 리소스가 드물고 비싼 경우, 프로그래머가 리소스를 더 이상 사용하지 않을 때 명시적으로 해제하면 성능이 향상될 수 있습니다. 명시적 제어 기능을 제공하려면 IDisposable에서 제공하는 Dispose를 구현합니다. 개체 소비자는 개체 사용이 끝나면 이 메서드를 호출해야 합니다. 해당 개체에 대한 다른 참조가 아직 유지되고 있는 경우에도 Dispose를 호출할 수 있습니다.

Dispose를 사용하여 명시적 제어 기능을 제공하는 경우에도 Finalize 메서드를 사용한 암시적 정리 기능을 제공해야 합니다. Finalize에서는 프로그래머가 Dispose 호출에 실패하는 경우 리소스가 누수되지 않도록 하는 백업 기능을 제공합니다.

FinalizeDispose를 구현하여 관리되지 않는 리소스를 정리하는 방법에 대한 자세한 내용은 가비지 수집을 참조하십시오. 다음 예제에서는 구현 하기 위한 기본 디자인 패턴 처리. 이 예제에서는 System 네임스페이스가 필요합니다.

' Design pattern for a base class.

Public Class Base
   Implements IDisposable
    ' Field to handle multiple calls to Dispose gracefully.
    Dim disposed as Boolean = false

   ' Implement IDisposable.
   Public Overloads Sub Dispose() Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)
   End Sub

   Protected Overloads Overridable Sub Dispose(disposing As Boolean)
      If disposed = False Then
          If disposing Then
             ' Free other state (managed objects).
             disposed = True
          End If
          ' Free your own state (unmanaged objects).
          ' Set large fields to null.
      End If
   End Sub

   Protected Overrides Sub Finalize()
      ' Simply call Dispose(False).
      Dispose (False)
   End Sub
End Class

' Design pattern for a derived class.
Public Class Derived
   Inherits Base

    ' Field to handle multiple calls to Dispose gracefully.
    Dim disposed as Boolean = false

   Protected Overloads Overrides Sub Dispose(disposing As Boolean)
      If disposed = False Then
          If disposing Then
             ' Release managed resources.
          End If
          ' Release unmanaged resources.
          ' Set large fields to null.
          disposed = True
      End If
      ' Call Dispose on your base class.
      Mybase.Dispose(disposing)
   End Sub
   ' The derived class does not have a Finalize method
   ' or a Dispose method without parameters because it inherits
   ' them from the base class.
End Class
// Design pattern for a base class.
public class Base: IDisposable
{
    private bool disposed = false;

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

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Free other state (managed objects).
            }
            // Free your own state (unmanaged objects).
            // Set large fields to null.
            disposed = true;
        }
    }

    // Use C# destructor syntax for finalization code.
    ~Base()
    {
        // Simply call Dispose(false).
        Dispose (false);
    }
}

// Design pattern for a derived class.
public class Derived: Base
{
    private bool disposed = false;

    protected override void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Release managed resources.
            }
            // Release unmanaged resources.
            // Set large fields to null.
           // Call Dispose on your base class.
            disposed = true;
        }
        base.Dispose(disposing);
    }
    // The derived class does not have a Finalize method
    // or a Dispose method without parameters because it inherits
    // them from the base class.
}

다른 방법으로 표시 하는 이전 예제를 확장 하는 다음 코드를 처리 호출 됩니다 때 Finalize 라고. Disposing 패턴의 단계는 콘솔에 출력 추적 됩니다. 할당 및 관리 되지 않는 리소스의 릴리스는 파생된 클래스에서 처리 됩니다.

Imports System
Imports System.Collections.Generic
Imports System.Runtime.InteropServices

' Design pattern for a base class.
Public MustInherit Class Base
    Implements IDisposable

    Private disposed as Boolean = false
    Private instName As String
    Private trackingList As List(Of Object)

    Public Sub New(instanceName As String, tracking As List(Of Object))
        MyClass.instName = instanceName
        trackingList = tracking
        trackingList.Add(Me)
    End Sub

    Public ReadOnly Property InstanceName() As String
        Get
            Return instName
        End Get
    End Property

    'Implement IDisposable.
    Public Overloads Sub Dispose() Implements IDisposable.Dispose
        Console.WriteLine(vbNewLine + "[{0}].Base.Dispose()", instName)
        Dispose(true)
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overloads Overridable Sub Dispose(disposing As Boolean)
        If disposed = False Then
            If disposing Then
                ' Free other state (managed objects).
                Console.WriteLine("[{0}].Base.Dispose(true)", instName)
                trackingList.Remove(Me)
                Console.WriteLine("[{0}] Removed from tracking list: {1:x16}",
                    instanceName, MyClass.GetHashCode())
            Else
                Console.WriteLine("[{0}].Base.Dispose(false)", instName)
            End If
            disposed = True
        End If
    End Sub

    Protected Overrides Sub Finalize()
        ' Simply call Dispose(False).
        Console.WriteLine(vbNewLine + "[{0}].Base.Finalize()", instName)
        Dispose(False)
    End Sub
End Class

' Design pattern for a derived class.
Public Class Derived
   Inherits Base

    Private disposed as Boolean = false
    Private umResource As IntPtr

    Public Sub New(instanceName As String, tracking As List(Of Object))
        MyBase.New(instanceName, tracking)
        ' Save the instance name as an unmanaged resource
        umResource = Marshal.StringToCoTaskMemAuto(instanceName)
    End Sub

    Protected Overloads Overrides Sub Dispose(disposing As Boolean)
        If disposed = False Then
            If disposing Then
                Console.WriteLine("[{0}].Derived.Dispose(true)", InstanceName)
                ' Release managed resources.
            Else
                Console.WriteLine("[{0}].Derived.Dispose(false)", InstanceName)
            End If
           ' Release unmanaged resources.
            If umResource <> IntPtr.Zero
                Marshal.FreeCoTaskMem(umResource)
                Console.WriteLine("[{0}] Unmanaged memory freed at {1:x16}", _
                    InstanceName, umResource.ToInt64())
                umResource = IntPtr.Zero
            End If
            disposed = True
        End If
        ' Call Dispose in the base class.
        MyBase.Dispose(disposing)
    End Sub
    ' The derived class does not have a Finalize method
    ' or a Dispose method without parameters because it inherits
    ' them from the base class.
End Class

Public Class TestDisposal
    Public Shared Sub Main()
        Dim tracking As New List(Of Object)()

        ' Dispose is not called, Finalize will be called later.
        Using Nothing
            Console.WriteLine(vbNewLine + "Disposal Scenario: #1" + vbNewLine)
            Dim d3 As New Derived("d1", tracking)
        End Using

        ' Dispose is implicitly called in the scope of the using statement.
        Using d1 As New Derived("d2", tracking)
            Console.WriteLine(vbNewLine + "Disposal Scenario: #2" + vbNewLine)
        End Using

        ' Dispose is explicitly called.
        Using Nothing
            Console.WriteLine(vbNewLine + "Disposal Scenario: #3" + vbNewLine)
            Dim d2 As New Derived("d3", tracking)
            d2.Dispose()
        End Using

        ' Again, Dispose is not called, Finalize will be called later.
        Using Nothing
            Console.WriteLine(vbNewLine + "Disposal Scenario: #4" + vbNewLine)
            Dim d4 As New Derived("d4", tracking)
        End Using

        ' List the objects remaining to dispose.
        Console.WriteLine(vbNewLine + "Objects remaining to dispose = {0:d}", tracking.Count)
        For Each dd As Derived in tracking
            Console.WriteLine("    Reference Object: {0:s}, {1:x16}",
                dd.InstanceName, dd.GetHashCode())
        Next dd
        ' Queued finalizers will be exeucted when Main() goes out of scope.
        Console.WriteLine(vbNewLine + "Dequeueing finalizers...")
    End Sub
End Class

' The program will display output similar to the following:
'
' Disposal Scenario: #1
'
'
' Disposal Scenario: #2
'
'
' [d2].Base.Dispose()
' [d2].Derived.Dispose(true)
' [d2] Unmanaged memory freed at 00000000001ce420
' [d2].Base.Dispose(true)
' [d2] Removed from tracking list: 0000000002bf8098
'
' Disposal Scenario: #3
'
'
' [d3].Base.Dispose()
' [d3].Derived.Dispose(true)
' [d3] Unmanaged memory freed at 00000000001ce420
' [d3].Base.Dispose(true)
' [d3] Removed from tracking list: 0000000000bb8560
'
' Disposal Scenario: #4
'
'
' Objects remaining to dispose = 2
'     Reference Object: d1, 000000000297b065
'     Reference Object: d4, 0000000003553390
'
' Dequeueing finalizers...
'
' [d4].Base.Finalize()
' [d4].Derived.Dispose(false)
' [d4] Unmanaged memory freed at 00000000001ce420
' [d4].Base.Dispose(false)
'
' [d1].Base.Finalize()
' [d1].Derived.Dispose(false)
' [d1] Unmanaged memory freed at 00000000001ce3f0
' [d1].Base.Dispose(false)
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

// Design pattern for a base class.
public abstract class Base : IDisposable
{
    private bool disposed = false;
    private string instanceName;
    private List<object> trackingList;

    public Base(string instanceName, List<object> tracking)
    {
        this.instanceName = instanceName;
         trackingList = tracking;
         trackingList.Add(this);
    }

    public string InstanceName
    {
        get
        {
            return instanceName;
        }
    }

    //Implement IDisposable.
    public void Dispose()
    {
        Console.WriteLine("\n[{0}].Base.Dispose()", instanceName);
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Free other state (managed objects).
                Console.WriteLine("[{0}].Base.Dispose(true)", instanceName);
                trackingList.Remove(this);
                Console.WriteLine("[{0}] Removed from tracking list: {1:x16}",
                    instanceName, this.GetHashCode());
            }
            else
            {
                Console.WriteLine("[{0}].Base.Dispose(false)", instanceName);
            }
            disposed = true;
        }
    }

    // Use C# destructor syntax for finalization code.
    ~Base()
    {
        // Simply call Dispose(false).
        Console.WriteLine("\n[{0}].Base.Finalize()", instanceName);
        Dispose(false);
    }
}

// Design pattern for a derived class.
public class Derived : Base
{
    private bool disposed = false;
    private IntPtr umResource;

    public Derived(string instanceName, List<object> tracking) :
        base(instanceName, tracking)
    {
         // Save the instance name as an unmanaged resource
         umResource = Marshal.StringToCoTaskMemAuto(instanceName);
    }

    protected override void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                Console.WriteLine("[{0}].Derived.Dispose(true)", InstanceName);
                // Release managed resources.
            }
            else
            {
                Console.WriteLine("[{0}].Derived.Dispose(false)", InstanceName);
            }
            // Release unmanaged resources.
            if (umResource != IntPtr.Zero)
            {
                Marshal.FreeCoTaskMem(umResource);
                Console.WriteLine("[{0}] Unmanaged memory freed at {1:x16}",
                    InstanceName, umResource.ToInt64());
                umResource = IntPtr.Zero;
            }
            disposed = true;
        }
        // Call Dispose in the base class.
        base.Dispose(disposing);
    }
    // The derived class does not have a Finalize method
    // or a Dispose method without parameters because it inherits
    // them from the base class.
}

public class TestDisposal
{
    public static void Main()
    {
        List<object> tracking = new List<object>();

        // Dispose is not called, Finalize will be called later.
        using (null)
        {
            Console.WriteLine("\nDisposal Scenario: #1\n");
            Derived d3 = new Derived("d1", tracking);
        }

        // Dispose is implicitly called in the scope of the using statement.
        using (Derived d1 = new Derived("d2", tracking))
        {
            Console.WriteLine("\nDisposal Scenario: #2\n");
        }

        // Dispose is explicitly called.
        using (null)
        {
            Console.WriteLine("\nDisposal Scenario: #3\n");
            Derived d2 = new Derived("d3", tracking);
            d2.Dispose();
        }

        // Again, Dispose is not called, Finalize will be called later.
        using (null)
        {
            Console.WriteLine("\nDisposal Scenario: #4\n");
            Derived d4 = new Derived("d4", tracking);
        }

        // List the objects remaining to dispose.
        Console.WriteLine("\nObjects remaining to dispose = {0:d}", tracking.Count);
        foreach (Derived dd in tracking)
        {
            Console.WriteLine("    Reference Object: {0:s}, {1:x16}",
                dd.InstanceName, dd.GetHashCode());
        }

        // Queued finalizers will be exeucted when Main() goes out of scope.
        Console.WriteLine("\nDequeueing finalizers...");
    }
}

// The program will display output similar to the following:
//
// Disposal Scenario: #1
//
//
// Disposal Scenario: #2
//
//
// [d2].Base.Dispose()
// [d2].Derived.Dispose(true)
// [d2] Unmanaged memory freed at 000000000034e420
// [d2].Base.Dispose(true)
// [d2] Removed from tracking list: 0000000002bf8098
//
// Disposal Scenario: #3
//
//
// [d3].Base.Dispose()
// [d3].Derived.Dispose(true)
// [d3] Unmanaged memory freed at 000000000034e420
// [d3].Base.Dispose(true)
// [d3] Removed from tracking list: 0000000000bb8560
//
// Disposal Scenario: #4
//
//
// Objects remaining to dispose = 2
//    Reference Object: d1, 000000000297b065
//    Reference Object: d4, 0000000003553390
//
// Dequeueing finalizers...
//
// [d4].Base.Finalize()
// [d4].Derived.Dispose(false)
// [d4] Unmanaged memory freed at 000000000034e420
// [d4].Base.Dispose(false)
//
// [d1].Base.Finalize()
// [d1].Derived.Dispose(false)
// [d1] Unmanaged memory freed at 000000000034e3f0
// [d1].Base.Dispose(false)

구현 하는 디자인 패턴을 보여주는 추가 코드 예제 FinalizeDispose, 참조 Dispose 메서드 구현.

Dispose 메서드 이름 사용자 지정

경우에 따라서는 Dispose 대신 도메인 고유 이름을 사용하는 것이 더 좋습니다. 예를 들어, 파일 캡슐화의 경우에는 Close라는 메서드 이름을 사용할 수 있습니다. 이 경우 Dispose를 전용으로 구현하고 Dispose를 호출하는 공용 Close 메서드를 만듭니다. 다음 코드 예제에서는 이러한 패턴을 보여 줍니다. 여기서 Close는 사용자의 도메인에 적절한 메서드 이름으로 바꿀 수 있습니다. 이 예제에서는 System 네임스페이스가 필요합니다.

' Do not make this method overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
   ' Call the Dispose method with no parameters.
   Dispose()
End Sub
// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{
   // Call the Dispose method with no parameters.
   Dispose();
}

Finalize

다음 규칙은 Finalize 메서드의 사용 지침을 요약한 것입니다.

  • 종료가 필요한 개체에 대해서만 Finalize를 구현합니다. Finalize 메서드를 구현하면 성능이 저하될 수 있습니다.

  • Finalize 메서드가 필요하면 클래스 사용자가 Finalize 메서드를 호출할 필요가 없도록 IDisposable을 구현할 것인지 고려해야 합니다.

  • Finalize 메서드를 보다 가시적인 메서드로 만들지 않습니다. 이 메서드는 public이 아니라 protected여야 합니다.

  • 개체의 Finalize 메서드는 해당 개체가 소유하는 모든 외부 리소스를 해제해야 합니다. 또한 Finalize 메서드는 해당 개체에서 보유한 리소스만 해제해야 합니다. Finalize 메서드는 다른 개체를 참조하지 않아야 합니다.

  • 해당 개체의 기본 클래스가 아닌 개체에 대해 Finalize 메서드를 직접 호출하지 않습니다. 이것은 C# 프로그래밍 언어에서는 올바른 작업이 아닙니다.

  • 개체의 Finalize 메서드에서 기본 클래스의 Finalize 메서드를 호출합니다.

    참고참고

    기본 클래스의 Finalize 메서드는 C# 및 C++ 소멸자 구문을 사용하여 자동으로 호출됩니다.

Dispose

다음 규칙은 Dispose 메서드의 사용 지침을 요약한 것입니다.

  • 명시적으로 해제되어야 하는 리소스를 캡슐화하는 형식에 대해 dispose 디자인 패턴을 구현합니다. 사용자는 공용 Dispose 메서드를 호출하여 외부 리소스를 해제할 수 있습니다.

  • 일반적으로 기본 형식에 리소스를 계속 사용하는 파생 형식이 있으면 기본 형식에서는 리소스를 계속 사용하지 않더라도 해당 기본 형식에 대해 dispose 디자인 패턴을 구현합니다. 기본 형식에 Close 메서드가 있으면 이는 대개 Dispose를 구현해야 함을 나타냅니다. 이런 경우에는 기본 형식에 대해 Finalize 메서드를 구현하지 않습니다. Finalize는 정리가 필요한 리소스를 사용하는 파생 형식에서 구현되어야 합니다.

  • 형식이 소유하는 삭제 가능한 모든 리소스는 해당 Dispose 메서드에서 해제합니다.

  • 인스턴스에 대해 Dispose를 호출한 후에는 GC.SuppressFinalize를 호출하여 Finalize 메서드가 실행되지 않도록 합니다. 드물지만 Dispose에서 다루지 않는 작업을 Finalize에서 수행해야 하는 경우에는 이 규칙이 적용되지 않습니다.

  • 기본 클래스에서 IDisposable을 구현하면 기본 클래스의 Dispose 메서드를 호출합니다.

  • Dispose가 호출될 것으로 가정하지 않습니다. 형식에서 소유하는 관리되지 않는 리소스는 Dispose가 호출되지 않는 경우 Finalize 메서드에서도 해제되어야 합니다.

  • 리소스가 이미 삭제되었을 때 해당 형식(Dispose 제외)에 대한 인스턴스 메서드에서 ObjectDisposedException을 throw합니다. Dispose 메서드는 예외를 throw하지 않고 여러 번 호출할 수 있으므로 이 규칙이 적용되지 않습니다.

  • 기본 형식의 계층 구조를 통해 Dispose에 대한 호출을 전파합니다. Dispose 메서드는 해당 개체와 해당 개체 소유의 모든 개체에서 사용하는 리소스를 모두 해제해야 합니다. 예를 들어, 사용자가 모르는 사이에 TextReader에 의해 만들어지는 Stream과 Encoding을 계속 사용하는 TextReader 등의 개체를 만들 수 있습니다. 또한 Stream과 Encoding은 모두 외부 리소스를 받아 들일 수 있습니다. TextReader에 대해 Dispose 메서드를 호출하면 해당 개체는 Stream과 Encoding에 대해 Dispose를 호출하여 해당 외부 리소스를 해제하도록 합니다.

  • Dispose 메서드가 호출된 다음에는 개체를 다시 사용할 수 없도록 하는 것이 좋습니다. 이미 삭제된 개체를 다시 만드는 것은 구현하기 어려운 패턴입니다.

  • Dispose 메서드는 예외를 throw하지 않고 한 번 이상 호출될 수 있도록 합니다. 이 메서드는 처음 호출된 이후에는 아무 작업도 수행하지 않습니다.

Portions Copyright 2005 Microsoft Corporation. All rights reserved.

Portions Copyright Addison-Wesley Corporation. All rights reserved.

디자인 지침에 자세한 내용은 참조를 "Framework 디자인 지침: 규칙, 숙어, 및 재사용에 대 한 패턴입니다.NET 라이브러리"도 서 Krzysztof Cwalina와 Brad Abrams, 게시 Addison-wesley, 2005.

참고 항목

참조

IDisposable.Dispose

Object.Finalize

개념

가비지 수집

기타 리소스

클래스 라이브러리 개발을 위한 디자인 지침

디자인 패턴