공용 언어 런타임의 GC(가비지 수집기)는 관리되는 개체에서 사용하는 메모리를 회수합니다. 일반적으로 관리되지 않는 리소스를 사용하는 형식은 관리되지 않는 리소스를 회수할 수 있도록 IDisposable 또는 IAsyncDisposable 인터페이스를 구현합니다. 구현 IDisposable하는 개체 사용을 마치면 개체 Dispose 또는 DisposeAsync 구현을 호출하여 명시적으로 정리를 수행합니다. 다음 두 가지 방법 중 하나로 이 작업을 수행할 수 있습니다.
- C#
using
문 또는 선언(Using
Visual Basic의 경우)을 사용합니다. -
try/finally
블록을 구현하고 Dispose에서 DisposeAsync 또는finally
메서드를 호출합니다.
중요합니다
GC는 또는 에 대한 지식이 없으므로 개체를 IDisposable.Dispose() 않습니다. GC는 개체를 종료할 수 있는지(즉, 메서드를 정의함 Object.Finalize() ) 및 개체의 종료자를 호출해야 하는 경우에만 알고 있습니다. 자세한 내용은 종료의 작동 방식을 참조하세요. 추가 세부사항은 Dispose
및 DisposeAsync
구현에 대해 참조하세요.
명시적으로 언급되지 않은 한, 변수 범위와 관계없이 System.IDisposable이나 System.IAsyncDisposable을 구현하는 개체는 항상 올바르게 처리되어야 합니다. 관리되지 않는 리소스를 해제하기 위해 종료자를 정의하는 형식은 일반적으로 GC.SuppressFinalize 또는 Dispose
구현에서 DisposeAsync
을(를) 호출합니다. 호출 SuppressFinalize 은 종료자가 이미 실행되었으며 종료를 위해 개체를 승격해서는 안 됨을 GC에 나타냅니다.
using 문장
C#의 문과 Visual Basic의 using
문은 Using
개체를 정리하기 위해 작성해야 하는 코드를 간소화합니다. 이 문은 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해야 합니다. 자세한 내용은 계단식 처분 구현을 참조하세요.
참고하십시오
.NET