다음을 통해 공유


IDisposable을 구현하는 개체 사용

공용 언어 런타임의 GC(가비지 수집기)는 관리되는 개체에서 사용하는 메모리를 회수합니다. 일반적으로 관리되지 않는 리소스를 사용하는 형식은 관리되지 않는 리소스를 회수할 수 있도록 IDisposable 또는 IAsyncDisposable 인터페이스를 구현합니다. 구현 IDisposable하는 개체 사용을 마치면 개체 Dispose 또는 DisposeAsync 구현을 호출하여 명시적으로 정리를 수행합니다. 다음 두 가지 방법 중 하나로 이 작업을 수행할 수 있습니다.

  • C# using 문 또는 선언(Using Visual Basic의 경우)을 사용합니다.
  • try/finally 블록을 구현하고 Dispose에서 DisposeAsync 또는 finally 메서드를 호출합니다.

중요합니다

GC는 또는 에 대한 지식이 없으므로 개체를 IDisposable.Dispose() 않습니다. GC는 개체를 종료할 수 있는지(즉, 메서드를 정의함 Object.Finalize() ) 및 개체의 종료자를 호출해야 하는 경우에만 알고 있습니다. 자세한 내용은 종료의 작동 방식을 참조하세요. 추가 세부사항은 DisposeDisposeAsync 구현에 대해 참조하세요.

명시적으로 언급되지 않은 한, 변수 범위와 관계없이 System.IDisposable이나 System.IAsyncDisposable을 구현하는 개체는 항상 올바르게 처리되어야 합니다. 관리되지 않는 리소스를 해제하기 위해 종료자를 정의하는 형식은 일반적으로 GC.SuppressFinalize 또는 Dispose 구현에서 DisposeAsync을(를) 호출합니다. 호출 SuppressFinalize 은 종료자가 이미 실행되었으며 종료를 위해 개체를 승격해서는 안 됨을 GC에 나타냅니다.

using 문장

C#의 문과 Visual Basic의 usingUsing 개체를 정리하기 위해 작성해야 하는 코드를 간소화합니다. 이 문은 using 하나 이상의 리소스를 가져오고, 지정한 문을 실행하고, 개체를 자동으로 삭제합니다. 그러나 이 using 문은 생성되는 메서드의 범위 내에서 사용되는 개체에만 유용합니다.

다음 예제에서는 using 문을 사용하여 System.IO.StreamReader 개체를 생성하고 해제합니다.

using System.IO;

class UsingStatement
{
    static void Main()
    {
        var buffer = new char[50];
        using (StreamReader streamReader = new("file1.txt"))
        {
            int charsRead = 0;
            while (streamReader.Peek() != -1)
            {
                charsRead = streamReader.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //
            }
        }
    }
}
Imports System.IO

Module UsingStatement
    Public Sub Main()
        Dim buffer(49) As Char
        Using streamReader As New StreamReader("File1.txt")
            Dim charsRead As Integer
            Do While streamReader.Peek() <> -1
                charsRead = streamReader.Read(buffer, 0, buffer.Length)
                ' 
                ' Process characters read.
                '
            Loop
        End Using
    End Sub
End Module

using 선언은 중괄호가 제거되고 범위 지정이 암시적일 때 사용할 수 있는 대체 구문입니다.

using System.IO;

class UsingDeclaration
{
    static void Main()
    {
        var buffer = new char[50];
        using StreamReader streamReader = new("file1.txt");

        int charsRead = 0;
        while (streamReader.Peek() != -1)
        {
            charsRead = streamReader.Read(buffer, 0, buffer.Length);
            //
            // Process characters read.
            //
        }
    }
}

클래스는 StreamReader 인터페이스를 구현하고 있으며, 이는 IDisposable가 관리되지 않는 리소스를 사용한다는 것을 나타냅니다. 하지만 이 예제에서는 StreamReader.Dispose 메서드를 명시적으로 호출하지 않습니다. C# 또는 Visual Basic 컴파일러가 using 문을 발견하면, 그것은 try/finally 블록을 명시적으로 포함하는 다음 코드와 동등한 중간 언어 (IL)를 내보낸다.

using System.IO;

class TryFinallyGenerated
{
    static void Main()
    {
        var buffer = new char[50];
        StreamReader? streamReader = null;
        try
        {
            streamReader = new StreamReader("file1.txt");
            int charsRead = 0;
            while (streamReader.Peek() != -1)
            {
                charsRead = streamReader.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //
            }
        }
        finally
        {
            // If non-null, call the object's Dispose method.
            streamReader?.Dispose();
        }
    }
}
Imports System.IO

Module TryFinallyGenerated
    Public Sub Main()
        Dim buffer(49) As Char
        Dim streamReader As New StreamReader("File1.txt")
        Try
            Dim charsRead As Integer
            Do While streamReader.Peek() <> -1
                charsRead = streamReader.Read(buffer, 0, buffer.Length)
                ' 
                ' Process characters read.
                '
            Loop
        Finally
            If streamReader IsNot Nothing Then DirectCast(streamReader, IDisposable).Dispose()
        End Try
    End Sub
End Module

또한 C# using 문을 사용하면 단일 문에서 여러 리소스를 획득할 수 있으며 이는 내부적으로 중첩된 using 문과 동일합니다. 다음 예제에서는 두 개체를 인스턴스화하여 서로 다른 두 StreamReader 파일의 내용을 읽습니다.

using System.IO;

class SingleStatementMultiple
{
    static void Main()
    {
        var buffer1 = new char[50];
        var buffer2 = new char[50];

        using StreamReader version1 = new("file1.txt"),
                           version2 = new("file2.txt");

        int charsRead1, charsRead2 = 0;
        while (version1.Peek() != -1 && version2.Peek() != -1)
        {
            charsRead1 = version1.Read(buffer1, 0, buffer1.Length);
            charsRead2 = version2.Read(buffer2, 0, buffer2.Length);
            //
            // Process characters read.
            //
        }
    }
}

Try/finally 차단

try/finally 블록을 using 문으로 래핑하는 대신, try/finally 블록을 직접 구현하도록 선택할 수 있습니다. 개인 코딩 스타일일 수도 있고, 다음 이유 중 하나로 이 작업을 수행할 수도 있습니다.

  • 예외가 catch 블록에서 throw된 경우 이를 처리하기 위해 try 블록을 포함하려면 그렇지 않으면 문 내에서 throw된 모든 예외가 using 처리되지 않습니다.
  • IDisposable를 구현하는 객체를 해당 범위가 선언된 블록에 국한되지 않게 인스턴스화합니다.

다음 예제는 try/catch/finally 블록을 사용하여 StreamReader 개체를 인스턴스화하고, 사용하고, 삭제하며, StreamReader 생성자와 그 ReadToEnd 메서드에서 발생하는 예외를 처리한다는 점을 제외하고 이전 예제와 비슷합니다. finally 블록의 코드는 IDisposable을(를) 구현하는 개체가 null가 아닌지 확인한 후 Dispose 메서드를 호출합니다. 이 작업을 수행하지 않으면 런타임에 예외가 NullReferenceException 발생할 수 있습니다.

using System;
using System.Globalization;
using System.IO;

class TryExplicitCatchFinally
{
    static void Main()
    {
        StreamReader? streamReader = null;
        try
        {
            streamReader = new StreamReader("file1.txt");
            string contents = streamReader.ReadToEnd();
            var info = new StringInfo(contents);
            Console.WriteLine($"The file has {info.LengthInTextElements} text elements.");
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("The file cannot be found.");
        }
        catch (IOException)
        {
            Console.WriteLine("An I/O error has occurred.");
        }
        catch (OutOfMemoryException)
        {
            Console.WriteLine("There is insufficient memory to read the file.");
        }
        finally
        {
            streamReader?.Dispose();
        }
    }
}
Imports System.Globalization
Imports System.IO

Module TryExplicitCatchFinally
    Sub Main()
        Dim streamReader As StreamReader = Nothing
        Try
            streamReader = New StreamReader("file1.txt")
            Dim contents As String = streamReader.ReadToEnd()
            Dim info As StringInfo = New StringInfo(contents)
            Console.WriteLine($"The file has {info.LengthInTextElements} text elements.")
        Catch e As FileNotFoundException
            Console.WriteLine("The file cannot be found.")
        Catch e As IOException
            Console.WriteLine("An I/O error has occurred.")
        Catch e As OutOfMemoryException
            Console.WriteLine("There is insufficient memory to read the file.")
        Finally
            If streamReader IsNot Nothing Then streamReader.Dispose()
        End Try
    End Sub
End Module

프로그래밍 언어가 try/finally 문을 지원하지 않지만 using 메서드에 대한 직접 호출을 허용하므로 Dispose 블록을 구현하거나 반드시 구현해야 하는 경우 이 기본 패턴을 따를 수 있습니다.

IDisposable 인스턴스 멤버

클래스가 인스턴스 필드 또는 속성을 소유하고 해당 형식이 구현되는 IDisposable경우 클래스도 구현 IDisposable해야 합니다. 자세한 내용은 계단식 처분 구현을 참조하세요.

참고하십시오