try-catch(C# 참조)try-catch (C# Reference)

try-catch 문은 try 블록에 이어 서로 다른 예외에 대한 처리기를 지정하는 하나 이상의 catch 절로 구성됩니다.The try-catch statement consists of a try block followed by one or more catch clauses, which specify handlers for different exceptions.

설명Remarks

예외가 throw되면 CLR(공용 언어 런타임)에서는 이 예외를 처리하는 catch 문을 검색합니다.When an exception is thrown, the common language runtime (CLR) looks for the catch statement that handles this exception. 현재 실행 중인 메서드에 catch 블록이 포함되지 않으면 CLR에서는 현재 메서드를 호출한 메서드 등에서 호출 스택까지 확인합니다.If the currently executing method does not contain such a catch block, the CLR looks at the method that called the current method, and so on up the call stack. catch 블록을 찾을 수 없으면 CLR에서는 처리되지 않은 예외 메시지를 사용자에게 표시하고 프로그램의 예외를 중지합니다.If no catch block is found, then the CLR displays an unhandled exception message to the user and stops execution of the program.

try 블록에는 예외를 가져올 수 있는 보호된 코드가 포함됩니다.The try block contains the guarded code that may cause the exception. 블록은 예외가 throw되거나 성공적으로 완료될 때까지 실행됩니다.The block is executed until an exception is thrown or it is completed successfully. 예를 들어 null 개체를 캐스팅하는 다음 시도에서 NullReferenceException 예외가 발생합니다.For example, the following attempt to cast a null object raises the NullReferenceException exception:

object o2 = null;
try
{
    int i2 = (int)o2;   // Error
}

catch 절은 예외 형식을 catch하는 인수 없이 사용할 수 있지만 이 사용은 권장되지 않습니다.Although the catch clause can be used without arguments to catch any type of exception, this usage is not recommended. 일반적으로 복구하는 방법을 알고 있는 예외만 catch해야 합니다.In general, you should only catch those exceptions that you know how to recover from. 따라서 항상 System.Exception에서 파생된 개체 인수를 지정해야 합니다. 예를 들면 다음과 같습니다.Therefore, you should always specify an object argument derived from System.Exception For example:

catch (InvalidCastException e)
{
}

같은 try-catch 문에서 특정 catch 절을 두 개 이상 사용할 수 있습니다.It is possible to use more than one specific catch clause in the same try-catch statement. 이 경우 catch 절은 순서대로 검사되므로 catch 절의 순서가 중요합니다.In this case, the order of the catch clauses is important because the catch clauses are examined in order. 더 구체적인 예외를 덜 구체적인 예외보다 먼저 catch합니다.Catch the more specific exceptions before the less specific ones. 나중에 나타나는 블록에 도달할 수 없도록 catch 블록 순서를 지정하면 컴파일러에서 오류가 발생합니다.The compiler produces an error if you order your catch blocks so that a later block can never be reached.

catch 인수를 사용하여 처리할 예외를 필터링할 수 있습니다.Using catch arguments is one way to filter for the exceptions you want to handle. 예외를 추가로 검사하는 예외 필터를 사용하여 예외를 처리할지 결정할 수도 있습니다.You can also use an exception filter that further examines the exception to decide whether to handle it. 예외 필터가 false를 반환하면 처리기 검색이 계속됩니다.If the exception filter returns false, then the search for a handler continues.

catch (ArgumentException e) when (e.ParamName == "…")
{
}

필터 덕분에 스택이 손상되지 않으므로 예외 필터는 catch하고 다시 throw하는 것이 좋습니다(아래 내용 참조).Exception filters are preferable to catching and rethrowing (explained below) because filters leave the stack unharmed. 나중에 나타나는 처리기가 스택을 덤프하면 예외가 다시 throw된 마지막 위치가 아니라 예외가 원래 발생한 위치를 확인할 수 있습니다.If a later handler dumps the stack, you can see where the exception originally came from, rather than just the last place it was rethrown. 예외 필터 식의 일반적으로 사용법은 로깅입니다.A common use of exception filter expressions is logging. 로그에도 출력되는 항상 false를 반환하는 필터를 만들 수 있고, 예외를 처리하고 다시 throw할 필요 없이 진행되는 대로 예외를 기록할 수 있습니다.You can create a filter that always returns false that also outputs to a log, you can log exceptions as they go by without having to handle them and rethrow.

throw 문을 catch 블록에서 사용하여 catch 문에 의해 catch된 예외를 다시 throw할 수 있습니다.A throw statement can be used in a catch block to re-throw the exception that is caught by the catch statement. 다음 예제에서는 IOException 예외에서 소스 정보를 추출하고 예외를 부모 메서드에 throw합니다.The following example extracts source information from an IOException exception, and then throws the exception to the parent method.

catch (FileNotFoundException e)
{
    // FileNotFoundExceptions are handled here.
}
catch (IOException e)
{
    // Extract some information from this exception, and then 
    // throw it to the parent method.
    if (e.Source != null)
        Console.WriteLine("IOException source: {0}", e.Source);
    throw;
}

하나의 예외를 catch하고 다른 예외를 throw할 수 있습니다.You can catch one exception and throw a different exception. 이 작업을 할 때 다음 예제와 같이 내부 예외로 catch한 예외를 지정합니다.When you do this, specify the exception that you caught as the inner exception, as shown in the following example.

catch (InvalidCastException e) 
{
    // Perform some action here, and then throw a new exception.
    throw new YourCustomException("Put your error message here.", e);
}

다음 예제와 같이 지정된 조건이 true이면 예외를 다시 throw할 수도 있습니다.You can also re-throw an exception when a specified condition is true, as shown in the following example.

catch (InvalidCastException e)
{
    if (e.Data == null)
    {
        throw;
    }
    else
    {
        // Take some action.
    }
}

참고

예외 필터를 사용하여 유사한 결과를 더 깔끔하게 얻을 수도 있습니다(물론 이 문서의 앞 부분에서 설명한 것처럼 스택을 수정하지 않음).It is also possible to use an exception filter to get a similar result in an often cleaner fashion (as well as not modifying the stack, as explained earlier in this document). 다음 예제에서는 이전 예제와 같은 호출자에 대한 유사한 동작을 보여 줍니다.The following example has a similar behavior for callers as the previous example. e.Datanull일 때 함수는 호출자에게 InvalidCastException을 다시 throw합니다.The function throws the InvalidCastException back to the caller when e.Data is null.

catch (InvalidCastException e) when (e.Data != null) 
{
    // Take some action.
}

try 블록 내부에서 선언된 변수만 초기화합니다.From inside a try block, initialize only variables that are declared therein. 그렇지 않으면 블록의 예외가 완료되기 전에 다른 예외가 발생할 수 있습니다.Otherwise, an exception can occur before the execution of the block is completed. 예를 들어 다음 코드 예제에서 n 변수는 try 블록 내부에서 초기화됩니다.For example, in the following code example, the variable n is initialized inside the try block. Write(n) 문의 try 블록 외부에서 이 변수를 사용하려고 하면 컴파일러 오류가 발생합니다.An attempt to use this variable outside the try block in the Write(n) statement will generate a compiler error.

static void Main() 
{
    int n;
    try 
    {
        // Do not initialize this variable here.
        n = 123;
    }
    catch
    {
    }
    // Error: Use of unassigned local variable 'n'.
    Console.Write(n);
}

catch에 대한 자세한 내용은 try-catch-finally를 참조하세요.For more information about catch, see try-catch-finally.

비동기 메서드의 예외Exceptions in Async Methods

비동기 메서드는 async 한정자를 통해 표시되고 대개 하나 이상의 await 식 및 문을 포함합니다.An async method is marked by an async modifier and usually contains one or more await expressions or statements. await 식은 await 연산자를 Task 또는 Task<TResult>에 적용합니다.An await expression applies the await operator to a Task or Task<TResult>.

컨트롤이 비동기 메서드의 await에 도달하면 대기 중인 작업이 완료될 때까지 메서드의 진행이 일시 중단됩니다.When control reaches an await in the async method, progress in the method is suspended until the awaited task completes. 작업이 완료되면 메서드가 실행이 다시 시작될 수 있습니다.When the task is complete, execution can resume in the method. 자세한 내용은 async 및 await를 사용한 비동기 프로그래밍비동기 프로그램의 제어 흐름을 참조하세요.For more information, see Asynchronous Programming with async and await and Control Flow in Async Programs.

await가 적용되는 완료된 작업은 작업을 반환하는 메서드의 처리되지 않은 예외로 인해 오류 상태에 있을 수 있습니다.The completed task to which await is applied might be in a faulted state because of an unhandled exception in the method that returns the task. 작업을 기다리면 예외가 throw됩니다.Awaiting the task throws an exception. 작업을 반환하는 비동기 프로세스가 취소되면 작업이 취소됨 상태로 종료될 수도 있습니다.A task can also end up in a canceled state if the asynchronous process that returns it is canceled. 취소된 작업을 기다리면 OperationCanceledException이 throw됩니다.Awaiting a canceled task throws an OperationCanceledException. 비동기 프로세스를 취소하는 방법에 대한 자세한 내용은 Async 응용 프로그램 미세 조정을 참조하세요.For more information about how to cancel an asynchronous process, see Fine-Tuning Your Async Application.

예외를 catch하려면 try 블록에서 작업을 기다리고 연결된 catch 블록에서 예외를 catch합니다.To catch the exception, await the task in a try block, and catch the exception in the associated catch block. 예제에 대해서는 "예제" 섹션을 참조하세요.For an example, see the "Example" section.

대기 중인 비동기 메서드에서 여러 예외가 발생했기 때문에 작업이 오류 상태에 있을 수 있습니다.A task can be in a faulted state because multiple exceptions occurred in the awaited async method. 예를 들어 작업은 Task.WhenAll 호출의 결과일 수 있습니다.For example, the task might be the result of a call to Task.WhenAll. 작업을 기다릴 때 예외 중 하나만 catch되고 catch될 예외를 예상할 수 없습니다.When you await such a task, only one of the exceptions is caught, and you can't predict which exception will be caught. 예제에 대해서는 "예제" 섹션을 참조하세요.For an example, see the "Example" section.

예제Example

다음 예제에서 try 블록에는 예외를 가져올 수 있는 ProcessString 메서드에 대한 호출이 포함됩니다.In the following example, the try block contains a call to the ProcessString method that may cause an exception. catch 절에는 화면에 메시지만 표시하는 예외 처리기가 포함됩니다.The catch clause contains the exception handler that just displays a message on the screen. throw 문이 MyMethod 내부에서 호출되면 시스템에서는 catch 문을 검색하고 메시지 Exception caught를 표시합니다.When the throw statement is called from inside MyMethod, the system looks for the catch statement and displays the message Exception caught.

class TryFinallyTest
{
    static void ProcessString(string s)
    {
        if (s == null)
        {
            throw new ArgumentNullException();
        }
    }

    static void Main()
    {
        string s = null; // For demonstration purposes.

        try
        {               
            ProcessString(s);
        }
        catch (Exception e)
        {
            Console.WriteLine("{0} Exception caught.", e);
        }
    }
}
/*
Output:
System.ArgumentNullException: Value cannot be null.
   at TryFinallyTest.Main() Exception caught.
 * */

예제Example

다음 예제에서는 두 catch 블록이 사용되고 먼저 나오는 가장 구체적인 예외가 catch됩니다.In the following example, two catch blocks are used, and the most specific exception, which comes first, is caught.

가장 덜 구체적인 예외를 catch하려면 ProcessString의 throw 문을 throw new Exception() 문으로 바꿔야 합니다.To catch the least specific exception, you can replace the throw statement in ProcessString with the following statement: throw new Exception().

가장 덜 구체적인 catch 블록을 예제에 첫 번째로 배치하면 다음 오류 메시지가 표시됩니다. A previous catch clause already catches all exceptions of this or a super type ('System.Exception').If you place the least-specific catch block first in the example, the following error message appears: A previous catch clause already catches all exceptions of this or a super type ('System.Exception').

class ThrowTest3
{
    static void ProcessString(string s)
    {
        if (s == null)
        {
            throw new ArgumentNullException();
        }
    }

    static void Main()
    {
        try
        {
            string s = null;
            ProcessString(s);
        }
        // Most specific:
        catch (ArgumentNullException e)
        {
            Console.WriteLine("{0} First exception caught.", e);
        }
        // Least specific:
        catch (Exception e)
        {
            Console.WriteLine("{0} Second exception caught.", e);
        }
    }
}
/*
 Output:
 System.ArgumentNullException: Value cannot be null.
 at Test.ThrowTest3.ProcessString(String s) ... First exception caught.
*/

예제Example

다음 예제에서는 비동기 메서드에 대한 예외 처리를 보여 줍니다.The following example illustrates exception handling for async methods. 비동기 작업에서 throw하는 예외를 catch하려면 try 블록에 await 식을 배치하고 catch 블록에서 예외를 catch합니다.To catch an exception that an async task throws, place the await expression in a try block, and catch the exception in a catch block.

예제에서 throw new Exception 줄의 주석 처리를 제거하여 예외 처리를 보여 줍니다.Uncomment the throw new Exception line in the example to demonstrate exception handling. 작업의 IsFaulted 속성이 True로 설정되고, 작업의 Exception.InnerException 속성이 예외로 설정되고, 예외가 catch 블록에서 catch됩니다.The task's IsFaulted property is set to True, the task's Exception.InnerException property is set to the exception, and the exception is caught in the catch block.

throw new OperationCancelledException 줄의 주석 처리를 제거하여 비동기 프로세스를 취소할 수 있을 때 발생하는 동작을 보여 줍니다.Uncomment the throw new OperationCancelledException line to demonstrate what happens when you cancel an asynchronous process. 작업의 IsCanceled 속성이 true로 설정되고, 예외가 catch 블록에서 catch됩니다.The task's IsCanceled property is set to true, and the exception is caught in the catch block. 이 예제에 적용되지 않는 몇몇 조건에서는 작업의 IsFaulted 속성이 true로 설정되고 IsCanceledfalse로 설정됩니다.Under some conditions that don't apply to this example, the task's IsFaulted property is set to true and IsCanceled is set to false.

public async Task DoSomethingAsync()
{
    Task<string> theTask = DelayAsync();

    try
    {
        string result = await theTask;
        Debug.WriteLine("Result: " + result);
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Exception Message: " + ex.Message);
    }
    Debug.WriteLine("Task IsCanceled: " + theTask.IsCanceled);
    Debug.WriteLine("Task IsFaulted:  " + theTask.IsFaulted);
    if (theTask.Exception != null)
    {
        Debug.WriteLine("Task Exception Message: "
            + theTask.Exception.Message);
        Debug.WriteLine("Task Inner Exception Message: "
            + theTask.Exception.InnerException.Message);
    }
}

private async Task<string> DelayAsync()
{
    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";
}

// 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 a OperationCanceledException or TaskCanceledException
// is thrown in the awaited method:
//   Exception Message: canceled
//   Task IsCanceled: True
//   Task IsFaulted:  False

예제Example

다음 예제에서는 여러 작업에서 여러 예외가 발생할 수 있는 경우 예외 처리를 보여 줍니다.The following example illustrates exception handling where multiple tasks can result in multiple exceptions. try 블록은 Task.WhenAll에 대한 호출에서 반환된 작업을 기다립니다.The try block awaits the task that's returned by a call to Task.WhenAll. WhenAll이 적용된 작업 세 개가 완료되면 작업이 완료됩니다.The task is complete when the three tasks to which WhenAll is applied are complete.

세 작업에서 각각 예외가 발생합니다.Each of the three tasks causes an exception. catch 블록은 Task.WhenAll에서 반환된 작업의 Exception.InnerExceptions 속성에 있는 예외를 반복합니다.The catch block iterates through the exceptions, which are found in the Exception.InnerExceptions property of the task that was returned by Task.WhenAll.

public async Task DoMultipleAsync()
{
    Task theTask1 = ExcAsync(info: "First Task");
    Task theTask2 = ExcAsync(info: "Second Task");
    Task theTask3 = ExcAsync(info: "Third Task");

    Task allTasks = Task.WhenAll(theTask1, theTask2, theTask3);

    try
    {
        await allTasks;
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Exception: " + ex.Message);
        Debug.WriteLine("Task IsFaulted: " + allTasks.IsFaulted);
        foreach (var inEx in allTasks.Exception.InnerExceptions)
        {
            Debug.WriteLine("Task Inner Exception: " + inEx.Message);
        }
    }
}

private async Task ExcAsync(string info)
{
    await Task.Delay(100);
    
    throw new Exception("Error-" + info);
}

// 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

C# 언어 사양C# language specification

자세한 내용은 C# 언어 사양을 참조하세요.For more information, see the C# Language Specification. C# 언어 사양은 C# 구문 및 사용법에 대한 신뢰할 수 있는 소스입니다.The language specification is the definitive source for C# syntax and usage.

참고 항목See also