Try...Catch...Finally 문(Visual Basic)

코드를 계속 실행하는 동안 특정 코드 블록에서 발생할 수 있는 일부 또는 모든 오류를 처리하는 방법을 제공합니다.

구문

Try
    [ tryStatements ]
    [ Exit Try ]
[ Catch [ exception [ As type ] ] [ When expression ]
    [ catchStatements ]
    [ Exit Try ] ]
[ Catch ... ]
[ Finally
    [ finallyStatements ] ]
End Try

부분

용어 정의
tryStatements 선택 사항. 오류가 발생할 수 있는 문입니다. 복합 문일 수 있습니다.
Catch 선택 사항. 여러 개의 Catch 블록이 허용됩니다. Try 블록을 처리할 때 예외가 throw되면 각 Catch 문을 텍스트 순서대로 검사하여 예외를 처리하는지 여부를 결정하며, exception은 throw된 예외를 나타냅니다.
exception 선택 사항. 임의의 변수 이름입니다. exception의 초기 값은 throw된 오류 값입니다. 발견된 오류를 지정하기 위해 Catch와 함께 사용됩니다. 생략하면 Catch 문이 예외를 catch합니다.
type 선택 사항. 클래스 필터의 형식을 지정합니다. exception의 값이 type으로 지정된 형식이거나 파생된 형식인 경우 식별자는 예외 개체에 바인딩됩니다.
When 선택 사항. When 절이 있는 Catch 문은 expressionTrue로 평가되는 경우에만 예외를 catch합니다. When 절은 예외의 형식을 확인한 후에만 적용되며, expression은 예외를 나타내는 식별자를 참조할 수 있다.
expression 선택 사항. 암시적으로 Boolean으로 변환 가능해야 합니다. 제네릭 필터를 설명하는 식입니다. 일반적으로 오류 번호를 기준으로 필터링하는 데 사용됩니다. 오류가 발생하는 상황을 지정하기 위해 When 키워드와 함께 사용됩니다.
catchStatements 선택 사항. 연결된 Try 블록에서 발생하는 오류를 처리하는 문입니다. 복합 문일 수 있습니다.
Exit Try 선택 사항. Try...Catch...Finally 구조를 벗어나는 키워드입니다. End Try 문 바로 다음의 코드로 실행이 다시 시작됩니다. Finally 문은 계속 실행됩니다. Finally 블록에서는 허용되지 않습니다.
Finally 선택 사항. Finally 블록은 실행이 Try...Catch 문의 일부를 벗어날 때 항상 실행됩니다.
finallyStatements 선택 사항. 다른 모든 오류 처리가 발생한 후에 실행되는 문입니다.
End Try Try...Catch...Finally 구조를 종료합니다.

설명

코드의 특정 섹션에서 특정 예외가 발생할 것으로 예상되는 경우 코드를 Try 블록에 넣고 Catch 블록을 사용하여 제어를 보존하고 예외가 발생할 경우 처리합니다.

Try…Catch 문은 Try 블록과 다양한 예외에 대한 처리기를 지정하는 하나 이상의 Catch 절로 구성됩니다. Try 블록에서 예외가 throw되면 Visual Basic은 예외를 처리하는 Catch 문을 찾습니다. 일치하는 Catch 문을 찾을 수 없으면 Visual Basic은 현재 메서드를 호출한 메서드를 검사하는 등 호출 스택을 검사합니다. Catch 블록이 없으면 Visual Basic은 사용자에게 처리되지 않은 예외 메시지를 표시하고 프로그램 실행을 중지합니다.

Try…Catch 문에 두 개 이상의 Catch 문을 사용할 수 있습니다. 이렇게 하면 Catch 절이 순서대로 검사되므로 순서가 중요합니다. Catch 더 구체적인 예외를 덜 구체적인 예외보다 먼저 catch합니다.

다음 Catch 문 조건은 가장 덜 구체적이며 Exception 클래스에서 파생되는 모든 예외를 catch합니다. 일반적으로 예상되는 특정 예외를 모두 catch한 후 이러한 변형 중 하나를 Try...Catch...Finally 구조의 마지막 Catch 블록으로 사용해야 합니다. 제어 흐름은 이러한 변형 중 하나를 따르는 Catch 블록에 도달할 수 없습니다.

  • typeException입니다. 예: Catch ex As Exception

  • 문에 exception 변수가 없습니다. 예: Catch

Try…Catch…Finally 문이 다른 Try 블록에 중첩되면 Visual Basic은 먼저 가장 안쪽 Try 블록의 각 Catch 문을 검사합니다. 일치하는 Catch 문이 없으면 외부 Try…Catch…Finally 블록의 Catch 문으로 검색이 진행됩니다.

Try 블록의 지역 변수는 별도의 블록이기 때문에 Catch 블록에서 사용할 수 없습니다. 둘 이상의 블록에서 변수를 사용하려면 Try...Catch...Finally 구조 외부에서 변수를 선언합니다.

Try…Catch…Finally 문은 IntelliSense 코드 조각으로 사용할 수 있습니다. 코드 조각 관리자에서 코드 패턴 - If, For Each, TryCatch, 속성 등을 확장한 다음 오류 처리(예외)를 확장합니다. 자세한 내용은 코드 조각을 참조하세요.

Finally 블록

Try 구조를 종료하기 전에 실행해야 하는 문이 하나 이상 있는 경우 Finally 블록을 사용합니다. 제어는 Try…Catch 구조를 벗어나기 직전에 Finally 블록으로 전달됩니다. 이는 Try 구조 내부 어디에서나 예외가 발생하는 경우에도 마찬가지입니다.

Finally 블록은 예외가 발생하더라도 실행해야 하는 코드를 실행하는 데 유용합니다. Try...Catch 블록이 종료되는 방식에 관계없이 제어가 Finally 블록으로 전달됩니다.

Finally 블록의 코드는 코드가 Try 또는 Catch 블록에서 Return 문을 발견하더라도 실행됩니다. 다음과 같은 경우 제어가 Try 또는 Catch 블록에서 해당 Finally 블록으로 전달되지 않습니다.

실행을 Finally 블록으로 명시적으로 전송하는 것은 유효하지 않습니다. 예외를 통하는 경우를 제외하고 Finally 블록 외부로 실행을 전송하는 것은 유효하지 않습니다.

Try 문에 Catch 블록이 하나도 포함되어 있지 않으면 Finally 블록을 포함해야 합니다.

특정 예외를 catch할 필요가 없는 경우 Using 문은 Try…Finally 블록처럼 작동하며 블록을 종료하는 방법에 관계없이 리소스 폐기를 보장합니다. 처리되지 않은 예외가 있는 경우에도 마찬가지입니다. 자세한 내용은 using 문을 참조하세요.

Exception 인수

Catch 블록 exception 인수는 Exception 클래스의 인스턴스이거나 Exception 클래스에서 파생된 클래스입니다. Exception 클래스 인스턴스는 Try 블록에서 발생한 오류에 해당합니다.

Exception 개체의 속성은 예외의 원인과 위치를 식별하는 데 도움이 됩니다. 예를 들어, StackTrace 속성은 예외를 발생시킨 호출된 메서드를 나열하여 코드에서 오류가 발생한 위치를 찾는 데 도움을 줍니다. Message는 예외를 설명하는 메시지를 반환합니다. HelpLink는 관련 도움말 파일에 대한 링크를 반환합니다. InnerException은 현재 예외를 발생시킨 Exception 개체를 반환하거나 원본 Exception이 없는 경우 Nothing을 반환합니다.

Try…Catch 문 사용 시 고려 사항

비정상적이거나 예상하지 못한 프로그램 이벤트의 발생을 알리는 데에만 Try…Catch 문을 사용합니다. 그 이유는 다음과 같습니다.

  • Catch런타임에 예외를 발생시키면 추가 오버헤드가 발생하며 예외를 방지하기 위한 사전 확인보다 속도가 느려질 수 있습니다.

  • Catch 블록이 올바르게 처리되지 않으면 예외가 사용자에게 올바르게 보고되지 않을 수 있습니다.

  • 예외 처리는 프로그램을 더욱 복잡하게 만듭니다.

발생할 가능성이 있는 조건을 확인하기 위해 항상 Try…Catch 문이 필요한 것은 아닙니다. 다음 예에서는 파일을 열려고 하기 전에 파일이 존재하는지 확인합니다. 이렇게 하면 OpenText 메서드에서 throw된 예외를 catch할 필요성이 줄어듭니다.

Private Sub TextFileExample(ByVal filePath As String)

    ' Verify that the file exists.
    If System.IO.File.Exists(filePath) = False Then
        Console.Write("File Not Found: " & filePath)
    Else
        ' Open the text file and display its contents.
        Dim sr As System.IO.StreamReader =
            System.IO.File.OpenText(filePath)

        Console.Write(sr.ReadToEnd)

        sr.Close()
    End If
End Sub

스레드로부터 안전한 로깅이나 적절한 메시지를 통해 Catch 블록의 코드가 사용자에게 예외를 올바르게 보고할 수 있는지 확인합니다. 그렇지 않으면 예외가 알려지지 않은 채로 남아 있을 수 있습니다.

비동기 메서드

메서드에 async 한정자를 표시하면 메서드에서 await 연산자를 사용할 수 있습니다. Await 연산자가 포함된 문은 대기 중인 작업이 완료될 때까지 메서드 실행을 일시 중지합니다. 작업은 진행 중인 작업을 나타냅니다. Await 연산자와 연결된 작업이 완료되면 동일한 메서드에서 실행이 다시 시작됩니다. 자세한 내용은 비동기 프로그램의 제어 흐름을 참조하세요.

Async 메서드에서 반환된 작업은 처리되지 않은 예외로 인해 완료되었음을 나타내는 오류 상태로 종료될 수 있습니다. 작업이 취소된 상태로 끝날 수도 있으며, 이로 인해 대기 식에서 OperationCanceledException이 삭제됩니다. 두 가지 형식의 예외를 catch하려면 작업과 연결된 Await 식을 Try 블록에 배치하고 Catch 블록에서 예외를 catch합니다. 이 항목의 뒷부분에 예가 제공됩니다.

여러 예외로 인해 오류가 발생했기 때문에 작업이 오류 상태에 있을 수 있습니다. 예를 들어 작업은 Task.WhenAll 호출의 결과일 수 있습니다. 그러한 작업을 기다릴 때 발견된 예외는 예외 중 하나일 뿐이며 어떤 예외가 발견될지 예측할 수 없습니다. 이 항목의 뒷부분에 예가 제공됩니다.

Await 식은 Catch 블록 또는 Finally 블록 내에 있을 수 없습니다.

반복기

반복기 함수 또는 Get 접근자는 컬렉션에 대해 사용자 지정 반복을 수행합니다. 반복기는 Yield 문을 사용하여 컬렉션의 각 요소를 한 번에 하나씩 반환합니다. For Each...Next 문을 사용하여 반복기 함수를 호출합니다.

Yield 문은 Try 블록 내에 있을 수 있습니다. Yield 문을 포함하는 Try 블록은 Catch 블록과 Finally 블록을 가질 수 있습니다. 예를 보려면 Try 블록을 참조하세요.

Yield 문은 Catch 블록 또는 Finally 블록 내에 있을 수 없습니다.

반복기 함수 외부의 For Each 본문에서 예외가 throw되면 반복기 함수의 Catch 블록이 실행되지 않지만 반복기 함수의 Finally 블록이 실행됩니다. 반복기 함수 내의 Catch 블록은 반복기 함수 내에서 발생하는 예외만 catch합니다.

부분 신뢰 상황

네트워크 공유에서 호스트되는 애플리케이션과 같은 부분 신뢰 상황에서 Try...Catch...Finally는 호출이 포함된 메서드가 호출되기 전에 발생하는 보안 예외를 catch하지 않습니다. 다음 예를 서버 공유에 배치하고 여기에서 실행하면 "System.Security.SecurityException: 요청 실패" 오류가 발생합니다. 보안 예외에 대한 자세한 내용은 SecurityException 클래스를 참조하세요.

Try
    Process.Start("http://www.microsoft.com")
Catch ex As Exception
    Console.WriteLine("Can't load Web page" & vbCrLf & ex.Message)
End Try

이러한 부분 신뢰 상황에서는 Process.Start 문을 별도의 Sub에 넣어야 합니다. Sub에 대한 초기 호출이 실패합니다. 이렇게 하면 Process.Start를 포함하는 Sub가 시작되고 보안 예외가 생성되기 전에 Try...Catch가 이를 catch할 수 있습니다.

예제

Try...Catch...Finally의 구조

다음 예에서는 Try...Catch...Finally 문의 구조를 보여 줍니다.

Public Sub TryExample()
    ' Declare variables.
    Dim x As Integer = 5
    Dim y As Integer = 0

    ' Set up structured error handling.
    Try
        ' Cause a "Divide by Zero" exception.
        x = x \ y

        ' This statement does not execute because program
        ' control passes to the Catch block when the
        ' exception occurs.
        Console.WriteLine("end of Try block")
    Catch ex As Exception
        ' Show the exception's message.
        Console.WriteLine(ex.Message)

        ' Show the stack trace, which is a list of methods
        ' that are currently executing.
        Console.WriteLine("Stack Trace: " & vbCrLf & ex.StackTrace)
    Finally
        ' This line executes whether or not the exception occurs.
        Console.WriteLine("in Finally block")
    End Try
End Sub

Try 블록에서 호출된 메서드의 예외

다음 예에서 CreateException 메서드는 NullReferenceException을 throw합니다. 예외를 생성하는 코드가 Try 블록에 없습니다. 따라서 CreateException 메서드는 예외를 처리하지 않습니다. CreateException 메서드에 대한 호출이 Try 블록에 있으므로 RunSample 메서드는 예외를 처리합니다.

이 예에는 가장 구체적인 것부터 가장 일반적인 것 순으로 순서가 지정된 여러 형식의 예외에 대한 Catch 문이 포함되어 있습니다.

Public Sub RunSample()
    Try
        CreateException()
    Catch ex As System.IO.IOException
        ' Code that reacts to IOException.
    Catch ex As NullReferenceException
        Console.WriteLine("NullReferenceException: " & ex.Message)
        Console.WriteLine("Stack Trace: " & vbCrLf & ex.StackTrace)
    Catch ex As Exception
        ' Code that reacts to any other exception.
    End Try
End Sub

Private Sub CreateException()
    ' This code throws a NullReferenceException.
    Dim obj = Nothing
    Dim prop = obj.Name

    ' This code also throws a NullReferenceException.
    'Throw New NullReferenceException("Something happened.")
End Sub

Catch When 문

다음 예에서는 Catch When 문을 사용하여 조건식을 필터링하는 방법을 보여 줍니다. 조건식이 True로 평가되면 Catch 블록의 코드가 실행됩니다.

Private Sub WhenExample()
    Dim i As Integer = 5

    Try
        Throw New ArgumentException()
    Catch e As OverflowException When i = 5
        Console.WriteLine("First handler")
    Catch e As ArgumentException When i = 4
        Console.WriteLine("Second handler")
    Catch When i = 5
        Console.WriteLine("Third handler")
    End Try
End Sub
' Output: Third handler

중첩 Try

다음 예에는 Try 블록에 포함된 Try…Catch 문이 있습니다. 내부 Catch 블록은 InnerException 속성 집합이 원래 예외로 설정된 예외를 throw합니다. 외부 Catch 블록은 자체 예외와 내부 예외를 보고합니다.

Private Sub InnerExceptionExample()
    Try
        Try
            ' Set a reference to a StringBuilder.
            ' The exception below does not occur if the commented
            ' out statement is used instead.
            Dim sb As System.Text.StringBuilder
            'Dim sb As New System.Text.StringBuilder

            ' Cause a NullReferenceException.
            sb.Append("text")
        Catch ex As Exception
            ' Throw a new exception that has the inner exception
            ' set to the original exception.
            Throw New ApplicationException("Something happened :(", ex)
        End Try
    Catch ex2 As Exception
        ' Show the exception.
        Console.WriteLine("Exception: " & ex2.Message)
        Console.WriteLine(ex2.StackTrace)

        ' Show the inner exception, if one is present.
        If ex2.InnerException IsNot Nothing Then
            Console.WriteLine("Inner Exception: " & ex2.InnerException.Message)
            Console.WriteLine(ex2.StackTrace)
        End If
    End Try
End Sub

비동기 메서드에 대한 예외 처리

다음 예제에서는 비동기 메서드에 대한 예외 처리를 보여 줍니다. 비동기 작업에 적용되는 예외를 catch하려면 Await 식이 호출자의 Try 블록에 있고 예외는 Catch 블록에서 catch됩니다.

예제에서 Throw New Exception 줄의 주석 처리를 제거하여 예외 처리를 보여 줍니다. 예외가 Catch 블록에서 catch되고 작업의 IsFaulted 속성이 True로 설정되고, 작업의 Exception.InnerException 속성이 예외로 설정됩니다.

Throw New OperationCancelledException 줄의 주석 처리를 제거하여 비동기 프로세스를 취소할 수 있을 때 발생하는 동작을 보여 줍니다. 예외가 Catch 블록에서 catch되고 작업의 IsCanceled 속성이 True로 설정됩니다. 그러나 이 예에 적용되지 않는 일부 조건에서는 IsFaultedTrue로 설정되고 IsCanceledFalse로 설정됩니다.

Public Async Function DoSomethingAsync() As Task
    Dim theTask As Task(Of String) = DelayAsync()

    Try
        Dim result As String = Await theTask
        Debug.WriteLine("Result: " & result)
    Catch ex As Exception
        Debug.WriteLine("Exception Message: " & ex.Message)
    End Try

    Debug.WriteLine("Task IsCanceled: " & theTask.IsCanceled)
    Debug.WriteLine("Task IsFaulted:  " & theTask.IsFaulted)
    If theTask.Exception IsNot Nothing Then
        Debug.WriteLine("Task Exception Message: " &
            theTask.Exception.Message)
        Debug.WriteLine("Task Inner Exception Message: " &
            theTask.Exception.InnerException.Message)
    End If
End Function

Private Async Function DelayAsync() As Task(Of String)
    Await Task.Delay(100)

    ' Uncomment each of the following lines to
    ' demonstrate exception handling.

    'Throw New OperationCanceledException("canceled")
    'Throw New Exception("Something happened.")
    Return "Done"
End Function


' Output when no exception is thrown in the awaited method:
'   Result: Done
'   Task IsCanceled: False
'   Task IsFaulted:  False

' Output when an Exception is thrown in the awaited method:
'   Exception Message: Something happened.
'   Task IsCanceled: False
'   Task IsFaulted:  True
'   Task Exception Message: One or more errors occurred.
'   Task Inner Exception Message: Something happened.

' Output when an OperationCanceledException or TaskCanceledException
' is thrown in the awaited method:
'   Exception Message: canceled
'   Task IsCanceled: True
'   Task IsFaulted:  False

비동기 메서드에서 여러 예외 처리

다음 예제에서는 여러 작업에서 여러 예외가 발생할 수 있는 경우 예외 처리를 보여 줍니다. Try 블록에는 Task.WhenAll이 반환한 작업에 대한 Await 식이 있습니다. Task.WhenAll이 적용된 세 가지 작업이 완료되면 작업이 완료됩니다.

세 작업에서 각각 예외가 발생합니다. Catch 블록은 Task.WhenAll이 반환한 작업의 Exception.InnerExceptions 속성에 있는 예외를 반복합니다.

Public Async Function DoMultipleAsync() As Task
    Dim theTask1 As Task = ExcAsync(info:="First Task")
    Dim theTask2 As Task = ExcAsync(info:="Second Task")
    Dim theTask3 As Task = ExcAsync(info:="Third Task")

    Dim allTasks As Task = Task.WhenAll(theTask1, theTask2, theTask3)

    Try
        Await allTasks
    Catch ex As Exception
        Debug.WriteLine("Exception: " & ex.Message)
        Debug.WriteLine("Task IsFaulted: " & allTasks.IsFaulted)
        For Each inEx In allTasks.Exception.InnerExceptions
            Debug.WriteLine("Task Inner Exception: " + inEx.Message)
        Next
    End Try
End Function

Private Async Function ExcAsync(info As String) As Task
    Await Task.Delay(100)

    Throw New Exception("Error-" & info)
End Function

' Output:
'   Exception: Error-First Task
'   Task IsFaulted: True
'   Task Inner Exception: Error-First Task
'   Task Inner Exception: Error-Second Task
'   Task Inner Exception: Error-Third Task

참고 항목