예외 처리 문 - throw, try-catch, try-finallytry-catch-finally

예외를 처리하려면 throwtry 문을 사용합니다. 예외를 throw하려면 throw을 사용합니다. 코드 블록 실행 중에 발생할 수 있는 예외를 catch하고 처리하려면 try을 사용합니다.

throw

throw 문은 예외를 throw합니다.

if (shapeAmount <= 0)
{
    throw new ArgumentOutOfRangeException(nameof(shapeAmount), "Amount of shapes must be positive.");
}

throw e; 문에서 식 e의 결과는 암시적으로 System.Exception으로 변환 가능해야 합니다.

기본 제공된 예외 클래스(예: ArgumentOutOfRangeException 또는 InvalidOperationException)를 사용할 수 있습니다. .NET은 특정 조건에서 예외를 throw하는 도우미 메서드(ArgumentNullException.ThrowIfNullArgumentException.ThrowIfNullOrEmpty)도 제공합니다. System.Exception에서 파생되는 고유한 예외 클래스를 정의할 수도 있습니다. 자세한 내용은 예외 만들기 및 throw를 참조하세요.

catch 블록 내에서 throw; 문을 사용하여 catch 블록에서 처리되는 예외를 다시 throw할 수 있습니다.

try
{
    ProcessShapes(shapeAmount);
}
catch (Exception e)
{
    LogError(e, "Shape processing failed.");
    throw;
}

참고 항목

throw;Exception.StackTrace 속성에 저장된 예외의 원래 스택 추적을 유지합니다. 이와 반대로, throw e;eStackTrace 속성을 업데이트합니다.

예외가 throw되면 CLR(공용 언어 런타임)은 이 예외를 처리할 수 있는 catch 블록을 찾습니다. 현재 실행된 메서드에 이러한 catch 블록이 포함되어 있지 않으면 CLR은 현재 메서드를 호출한 메서드를 살펴보는 등 호출 스택을 살펴봅니다. catch 블록이 발견되지 않으면 CLR은 실행 스레드를 종료합니다. 자세한 내용은 C# 언어 사양예외 처리 방법 섹션을 참조하세요.

throw

throw를 식으로 사용할 수도 있습니다. 이는 다음과 같은 여러 경우에 편리할 수 있습니다.

  • 조건 연산자. 다음 예에서는 전달된 배열 args가 비어 있을 때 throw 식을 사용하여 ArgumentException을 throw합니다.

    string first = args.Length >= 1 
        ? args[0]
        : throw new ArgumentException("Please supply at least one argument.");
    
  • Null 병합 연산자. 다음 예에서는 속성에 할당할 문자열이 null인 경우 throw 식을 사용하여 ArgumentNullException을 throw합니다.

    public string Name
    {
        get => name;
        set => name = value ??
            throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
    }
    
  • 식 본문 람다 또는 메서드. 다음 예에서는 throw 식을 사용하여 InvalidCastException을 throw하여 DateTime 값으로의 변환이 지원되지 않음을 나타냅니다.

    DateTime ToDateTime(IFormatProvider provider) =>
             throw new InvalidCastException("Conversion to a DateTime is not supported.");
    

try

다음 형식 중 하나로 try 문을 사용할 수 있습니다. try-catch - try 블록 내에서 코드를 실행하는 동안 발생할 수 있는 예외를 처리합니다. try-finally - 제어가 try 블록을 떠날 때 실행되는 코드를 지정합니다. try-catch-finally - 앞의 두 가지 형식을 결합한 형태입니다.

try-catch

코드 블록 실행 중에 발생할 수 있는 예외를 처리하려면 try-catch 문을 사용합니다. try 블록 내에 예외가 발생할 수 있는 코드를 배치합니다. 해당 catch 블록에서 처리하려는 예외의 기본 형식을 지정하려면 catch 절을 사용합니다.

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}

여러 catch 절을 제공할 수 있습니다.

try
{
    var result = await ProcessAsync(-3, 4, cancellationToken);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}
catch (OperationCanceledException)
{
    Console.WriteLine("Processing is cancelled.");
}

예외가 발생하면 catch 절은 지정된 순서에 따라 위에서 아래로 검사됩니다. throw된 예외에 대해 최대 하나의 catch 블록만 실행됩니다. 앞의 예에서도 볼 수 있듯이 예외 변수 선언을 생략하고 catch 절에 예외 형식만 지정할 수 있습니다. 지정된 예외 형식이 없는 catch 절은 모든 예외와 일치하며, 있는 경우 마지막 catch 절이어야 합니다.

발생한 예외를 다시 throw하려면 다음 예와 같이 throw을 사용합니다.

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e)
{
    LogError(e, "Processing failed.");
    throw;
}

참고 항목

throw;Exception.StackTrace 속성에 저장된 예외의 원래 스택 추적을 유지합니다. 이와 반대로, throw e;eStackTrace 속성을 업데이트합니다.

when 예외 필터

예외 형식과 함께 예외를 추가로 검사하고 해당 catch 블록이 해당 예외를 처리하는지 결정하는 예외 필터를 지정할 수도 있습니다. 예외 필터는 다음 예와 같이 when 키워드 뒤에 오는 부울 식입니다.

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e) when (e is ArgumentException || e is DivideByZeroException)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}

앞의 예에서는 예외 필터를 사용하여 지정된 두 가지 형식의 예외를 처리하는 단일 catch 블록을 제공합니다.

예외 필터로 구분되는 경우 동일한 예외 형식에 대해 여러 개의 catch 절을 제공할 수 있습니다. 해당 조항 중 하나에는 예외 필터가 없을 수 있습니다. 그러한 절이 존재하는 경우 해당 예외 형식을 지정하는 절 중 마지막 절이어야 합니다.

catch 절에 예외 필터가 있는 경우 뒤에 나타나는 catch 절의 예외 형식과 동일하거나 그보다 적게 파생된 예외 형식을 지정할 수 있습니다. 예를 들어, 예외 필터가 있는 경우 catch (Exception e) 절이 마지막 절일 필요는 없습니다.

비동기 및 반복기 메서드의 예외

비동기 함수에서 예외가 발생하면 다음 예와 같이 함수 결과를 대기할 때 예외가 함수 호출자에게 전파됩니다.

public static async Task Run()
{
    try
    {
        Task<int> processing = ProcessAsync(-1);
        Console.WriteLine("Launched processing.");

        int result = await processing;
        Console.WriteLine($"Result: {result}.");
    }
    catch (ArgumentException e)
    {
        Console.WriteLine($"Processing failed: {e.Message}");
    }
    // Output:
    // Launched processing.
    // Processing failed: Input must be non-negative. (Parameter 'input')
}

private static async Task<int> ProcessAsync(int input)
{
    if (input < 0)
    {
        throw new ArgumentOutOfRangeException(nameof(input), "Input must be non-negative.");
    }

    await Task.Delay(500);
    return input;
}

반복기 메서드에서 예외가 발생하면 반복기가 다음 요소로 이동할 때만 호출자에게 전파됩니다.

try-finally

try-finally 문에서 제어가 try 블록을 벗어날 때 finally 블록이 실행됩니다. 다음의 결과로 제어가 try 블록을 떠날 수 있습니다.

  • 일반 실행,
  • 점프 문(즉, return, break, continue 또는 goto) 실행 또는
  • try 블록 외부로 예외 전파.

다음 예에서는 제어가 메서드를 떠나기 전에 finally 블록을 사용하여 개체의 상태를 다시 설정합니다.

public async Task HandleRequest(int itemId, CancellationToken ct)
{
    Busy = true;

    try
    {
        await ProcessAsync(itemId, ct);
    }
    finally
    {
        Busy = false;
    }
}

finally 블록을 사용하여 try 블록에 사용된 할당된 리소스를 정리할 수도 있습니다.

참고 항목

리소스 형식이 IDisposable 또는 IAsyncDisposable 인터페이스를 구현하는 경우 using을 고려합니다. using 문은 제어가 using 문을 벗어날 때 획득한 리소스가 삭제되도록 보장합니다. 컴파일러는 using 문을 try-finally 문으로 변환합니다.

거의 모든 경우에 finally 블록이 실행됩니다. finally 블록이 실행되지 않는 유일한 경우는 프로그램이 즉시 종료되는 것입니다. 예를 들어, 이러한 종료는 Environment.FailFast 호출이나 OverflowException 또는 InvalidProgramException 예외로 인해 발생할 수 있습니다. 대부분의 운영 체제는 프로세스 중지 및 언로드의 일부로 합리적인 리소스 정리를 수행합니다.

try-catch-finally

try-catch-finally 문을 사용하여 try 블록 실행 중에 발생할 수 있는 예외를 처리하고 제어가 try 문을 벗어날 때 실행되어야 하는 코드를 지정합니다.

public async Task ProcessRequest(int itemId, CancellationToken ct)
{
    Busy = true;

    try
    {
        await ProcessAsync(itemId, ct);
    }
    catch (Exception e) when (e is not OperationCanceledException)
    {
        LogError(e, $"Failed to process request for item ID {itemId}.");
        throw;
    }
    finally
    {
        Busy = false;
    }

}

catch 블록에서 예외를 처리하는 경우 finally 블록은 해당 catch 블록 실행 후에 실행됩니다(catch 블록 실행 중에 다른 예외가 발생하더라도). catchfinally 블록에 대한 자세한 내용은 각각 try-catchtry-finally 섹션을 참조하세요.

C# 언어 사양

자세한 내용은 C# 언어 사양의 다음 섹션을 참조하세요.

참고 항목