연습 - 특정 예외 형식 catch

완료됨

이 모듈의 앞부분에서 C# 애플리케이션에서 catch한 예외 개체가 예외 클래스의 인스턴스라는 것을 배웠습니다. 일반적으로 코드는 catch 다음 중 하나입니다.

  • 기본 클래스의 인스턴스 System.Exception 인 예외 개체입니다.
  • 기본 클래스에서 상속되는 예외 형식의 인스턴스인 예외 개체입니다. 예를 들어 InvalidCastException 클래스의 인스턴스입니다.

예외 속성 검사

System.Exception 는 모든 파생 예외 형식이 상속하는 기본 클래스입니다. 각 예외 형식은 특정 클래스 계층을 통해 기본 클래스에서 상속됩니다. 예를 들어 클래스 계층 구조 InvalidCastException 는 다음과 같습니다.

Object
    Exception
        SystemException
            InvalidCastException

대부분의 Exception를 상속하는 예외 클래스는 추가 기능을 제공하지 않으며 단순히 Exception에서 상속됩니다. 따라서 클래스의 Exception 속성을 검사하면 대부분의 예외와 코드에서 예외를 사용하는 방법을 이해할 수 있습니다.

클래스의 속성은 다음과 같습니다.Exception

  • 데이터: 속성은 Data 키-값 쌍에 임의의 데이터를 보유합니다.
  • HelpLink: 이 HelpLink 속성을 사용하여 예외의 원인에 대한 광범위한 정보를 제공하는 도움말 파일에 대한 URL(또는 URN)을 보유할 수 있습니다.
  • HResult: 이 HResult 속성을 사용하여 특정 예외에 할당된 코딩된 숫자 값에 액세스할 수 있습니다.
  • InnerException: 이 InnerException 속성을 사용하여 예외를 처리하는 동안 일련의 예외를 만들고 유지할 수 있습니다.
  • 메시지: 속성은 Message 예외의 원인에 대한 세부 정보를 제공합니다.
  • 원본: 이 Source 속성을 사용하여 애플리케이션의 이름 또는 오류를 유발하는 개체에 액세스할 수 있습니다.
  • StackTrace: 속성에는 StackTrace 오류가 발생한 위치를 확인하는 데 사용할 수 있는 스택 추적이 포함되어 있습니다.
  • TargetSite: 이 TargetSite 속성을 사용하여 현재 예외를 throw하는 메서드를 가져올 수 있습니다.

예외 속성, 기본 클래스 및 상속에 대한 이 검사에 약간 과부하가 걸리는 경우 괜찮습니다. 코드에서 예외를 포착하고 예외의 속성에 액세스하는 것이 예외와 예외 속성의 작동 방식을 설명하는 것보다 더 쉽습니다.

비고

이 모듈에서는 예외의 메시지 속성을 사용하여 애플리케이션의 사용자 인터페이스에서 예외를 보고하는 데 집중합니다.

예외 개체의 속성에 액세스

이제 예외 개체와 해당 속성을 이해했으므로 코딩을 시작해야 합니다.

  1. 다음과 같이 Program.cs 파일을 업데이트합니다.

    try
    {
        Process1();
    }
    catch
    {
        Console.WriteLine("An exception has occurred");
    }
    
    Console.WriteLine("Exit program");
    
    static void Process1()
    {
        try
        {
            WriteMessage();
        }
        catch
        {
            Console.WriteLine("Exception caught in Process1");
        }
    }
    
    static void WriteMessage()
    {
        double float1 = 3000.0;
        double float2 = 0.0;
        int number1 = 3000;
        int number2 = 0;
    
        Console.WriteLine(float1 / float2);
        Console.WriteLine(number1 / number2);
    }
    
  2. 코드를 검토하는 데 1분 정도 걸릴 수 있습니다.

    이전 단원에서 본 것과 동일한 코드입니다(챌린지 활동에 대한 솔루션 코드). 메서드 WriteMessage 중에 예외가 발생한다는 것을 알고 있습니다. 예외가 Process1 메서드에서 처리된다는 것을 또한 알고 있습니다. 이 코드를 사용하여 예외 개체 및 특정 예외 유형을 검사합니다.

  3. 다음과 같이 Process1 메서드를 업데이트합니다.

    static void Process1()
    {
        try
        {
            WriteMessage();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception caught in Process1: {ex.Message}");
        }
    }
    
  4. 잠시 시간을 내어 업데이트를 검토합니다.

    업데이트된 catch 절은 ex라는 개체에서 Exception 클래스의 인스턴스를 catch합니다. 또한 Console.WriteLine() 메서드를 사용하여 ex 개체의 Message 속성에 액세스하고, 오류 메시지를 콘솔에 표시합니다.

    인수 없이 절을 catch 사용할 수 있지만 이 방법은 권장되지 않습니다. 인수를 지정하지 않으면 모든 예외 유형이 catch되어 서로 구분할 수 없습니다.

    일반적으로 코드에서 복구 방법을 알고 있는 예외만 catch해야 합니다. 따라서 System.Exception에서 파생된 catch 개체 인수를 절에서 지정해야 합니다. 예외 유형은 가능한 한 구체적이어야 합니다. 이렇게 하면 예외 처리기가 해결할 수 없는 예외를 포착하지 않도록 막을 수 있습니다. 이 연습의 이후에 특정 예외 유형을 catch하도록 코드를 업데이트합니다.

  5. 파일 메뉴에서 저장을 선택합니다.

  6. 다음 코드 줄에 중단점을 설정합니다.

    Console.WriteLine($"Exception caught in Process1: {ex.Message}");
    
  7. 실행 메뉴에서 디버깅 시작을 선택합니다.

    코드 실행은 중단점에서 일시 중지되어야 합니다.

  8. 마우스 커서를 ex 위에 올리세요.

    IntelliSense는 이전에 검사한 것과 동일한 예외 속성을 표시합니다.

  9. 잠시 시간을 내어 예외 개체를 설명하는 정보를 검사합니다 ex.

    예외가 System.DivideByZeroException 예외 형식임을 주의하고, Message 속성이 Attempted to divide by zero.로 설정되어 있는지 확인하십시오.

    이 속성은 StackTrace 오류가 발생한 메서드 및 줄 번호와 오류를 초래한 메서드 호출(및 줄 번호)의 시퀀스를 보고합니다.

  10. 디버그 도구 모음에서 [계속]을 선택합니다.

  11. 콘솔 출력을 검사하는 데 1분 정도 소요됩니다.

    예외의 Message 속성은 애플리케이션에서 생성된 출력에 포함됩니다.

    ∞
    Exception caught in Process1: Attempted to divide by zero.
    Exit program
    

특정 예외 유형 잡기

이제 catch할 예외 유형을 알게 되었으므로 catch 절을 해당 특정 예외 유형을 처리하도록 업데이트할 수 있습니다.

  1. 다음과 같이 Process1 메서드를 업데이트합니다.

    static void Process1()
    {
        try
        {
            WriteMessage();
        }
        catch (DivideByZeroException ex)
        {
            Console.WriteLine($"Exception caught in Process1: {ex.Message}");
        }
    }
    
  2. 코드를 저장한 다음 디버그 세션을 시작합니다.

  3. 업데이트된 애플리케이션이 동일한 메시지를 콘솔에 보고합니다.

    보고된 메시지는 동일하지만 중요한 차이점이 있습니다. Process1 메서드는 준비된 특정 형식의 예외만 처리합니다.

  4. 다른 예외 형식을 생성하려면 다음과 같이 메서드를 WriteMessage 업데이트합니다.

    static void WriteMessage()
    {
        double float1 = 3000.0;
        double float2 = 0.0;
        int number1 = 3000;
        int number2 = 0;
        byte smallNumber;
    
        Console.WriteLine(float1 / float2);
        // Console.WriteLine(number1 / number2);
        checked
        {
            smallNumber = (byte)number1;
        }
    }
    
  5. checked 문장을 사용하고 있는지 주의하십시오.

    한 정수 계열 형식의 값을 다른 정수 형식에 할당하는 정수 계열 형식 계산을 수행하는 경우 결과는 오버플로 검사 컨텍스트에 따라 달라집니다. checked 컨텍스트에서 원본 값이 대상 형식의 범위 내에 있으면 변환에 성공합니다. 그렇지 않으면 OverflowException이(가) throw됩니다. 선택되지 않은 컨텍스트에서 변환은 항상 성공하며 다음과 같이 진행됩니다.

    • 원본 형식이 대상 형식보다 큰 경우 가장 중요한 "추가" 비트를 삭제하여 원본 값이 잘립니다. 그런 다음, 결과는 대상 형식의 값으로 처리됩니다.

    • 원본 형식이 대상 형식보다 작은 경우 원본 값은 대상 형식과 크기가 같도록 기호 확장 또는 0 확장입니다. 원본 형식이 서명된 경우 서명 확장이 사용됩니다. 원본 형식이 서명되지 않은 경우 0 확장이 사용됩니다. 그런 다음, 결과는 대상 형식의 값으로 처리됩니다.

    • 원본 형식이 대상 형식과 크기가 같으면 원본 값이 대상 형식의 값으로 처리됩니다.

    비고

    코드 블록 내에 checked 없는 정수 형식 계산은 코드 블록 내에 unchecked 있는 것처럼 처리됩니다.

  6. 코드를 저장한 다음 디버그 세션을 시작합니다.

  7. 새 예외 형식은 Process1 메서드 내부가 아닌 최상위 문에서 catch 절에 의해 catch됩니다.

    애플리케이션은 콘솔에 다음 메시지를 출력합니다.

    ∞
    An exception has occurred
    Exit program
    

    비고

    Process1catch 블록이 실행되지 않습니다. 이것은 당신이 원하는 동작입니다. 코드가 핸들할 준비가 된 예외만 catch합니다.

코드 블록에서 여러 예외를 처리하다

이 시점에서 단일 코드 블록에서 여러 예외가 발생할 때 어떤 일이 발생하는지 궁금할 수 있습니다. 코드가 각 예외가 발생할 때마다 catch 처리하나요?

  1. 다음과 같이 WriteMessage 메서드를 업데이트합니다.

    static void WriteMessage()
    {
        double float1 = 3000.0;
        double float2 = 0.0;
        int number1 = 3000;
        int number2 = 0;
        byte smallNumber;
    
        Console.WriteLine(float1 / float2);
        Console.WriteLine(number1 / number2);
        checked
        {
            smallNumber = (byte)number1;
        }
    }
    
  2. 다음 코드 줄에서 WriteMessage() 메서드 내부에 중단점을 설정합니다.

    Console.WriteLine(float1 / float2);
    
  3. 코드를 저장한 다음 디버그 세션을 시작합니다.

  4. 코드를 한 번에 한 줄씩 단계별로 실행하고 코드가 첫 번째 예외를 처리한 후 어떤 일이 발생하는지 확인합니다.

    첫 번째 예외가 발생하면 예외를 처리할 수 있는 첫 번째 catch 절에 컨트롤이 전달됩니다. 두 번째 예외를 생성하는 코드에 도달하지 않습니다. 즉, 일부 코드는 실행되지 않습니다. 이로 인해 심각한 문제가 발생할 수 있습니다.

  5. 잠시 시간을 내어 여러 예외를 관리하는 방법과 코드에서 여러 예외를 관리하지 않으려는 경우/이유를 고려합니다.

    이 모듈의 앞부분에서 예외는 가능한 한 발생 위치에 가까운 곳에서 캐치되어야 한다는 것을 배웠습니다. 이 점을 염두에 두고 자체 WriteMessage 메서드를 사용하여 예외를 잡도록 try-catch 메서드를 업데이트할 수 있습니다. 다음은 그 예입니다.

    static void WriteMessage()
    {
        double float1 = 3000.0;
        double float2 = 0.0;
        int number1 = 3000;
        int number2 = 0;
        byte smallNumber;
    
        try
        {
            Console.WriteLine(float1 / float2);
            Console.WriteLine(number1 / number2);
        }
        catch (DivideByZeroException ex)
        {
            Console.WriteLine($"Exception caught in WriteMessage: {ex.Message}");
        }
        checked
        {
            smallNumber = (byte)number1;
        }
    }
    

    또한 WriteMessage() 메서드 내의 별도의 try-catch에서 OverflowException을 발생시키는 코드를 래핑할 수 있습니다.

    checked
    {
        try
        {
            smallNumber = (byte)number1;
        }
        catch (OverflowException ex)
        {
            Console.WriteLine($"Exception caught in WriteMessage: {ex.Message}");
        }  
    }
    
  6. 후속 예외를 catch 하는 것이 바람직하지 않은 조건은 무엇인가요?

    메서드(또는 코드 블록)가 두 부분으로 구성된 프로세스를 완료하는 경우를 고려합니다. 프로세스의 두 번째 부분이 첫 번째 부분 완료에 종속되어 있다고 가정합니다. 프로세스의 첫 번째 부분을 성공적으로 완료할 수 없는 경우 프로세스의 두 번째 부분으로 계속 진행할 필요가 없습니다. 이 경우 더 큰 프로세스의 나머지 부분이나 일부를 시도하지 않고 오류 조건을 설명하는 메시지를 사용자에게 제공하는 것이 좋습니다.

각각의 예외 타입을 코드 블록에서 처리하기

데이터의 변형으로 인해 다른 유형의 예외가 발생할 수 있는 경우가 있습니다.

  1. 중단점을 지우고 Program.cs 파일의 내용을 다음 코드로 바꿉니다.

    // inputValues is used to store numeric values entered by a user
    string[] inputValues = new string[]{"three", "9999999999", "0", "2" };
    
    foreach (string inputValue in inputValues)
    {
        int numValue = 0;
        try
        {
            numValue = int.Parse(inputValue);
        }
        catch (FormatException)
        {
            Console.WriteLine("Invalid readResult. Please enter a valid number.");
        }
        catch (OverflowException)
        {
            Console.WriteLine("The number you entered is too large or too small.");
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
    
  2. 이 코드를 검토하려면 잠시 시간을 내세요.

    먼저 코드는 .라는 inputValues문자열 배열을 만듭니다. 배열의 데이터는 숫자 값을 입력하라는 지시를 받은 사용자가 입력한 값을 나타내기 위한 것입니다. 입력한 값에 따라 다른 예외 유형이 발생할 수 있습니다.

    코드는 메서드를 int.Parse 사용하여 문자열 "input" 값을 정수로 변환합니다. int.Parse 코드는 try 코드 블록 안에 배치됩니다.

  3. 다음 코드 줄에 중단점을 설정합니다.

    int numValue = 0;
    
  4. 코드를 저장한 다음 디버그 세션을 시작합니다.

  5. 코드를 한 줄씩 단계별로 실행하면서 다양한 예외 유형이 어떻게 잡히는지를 확인하십시오.

요약

이 단원에서 기억해야 하는 몇 가지 중요한 사항은 다음과 같습니다.

  • catch 절을 특정 예외 형식을 catch하도록 구성해야 합니다. 예를 들어 예외 유형입니다 DivideByZeroException .
  • 예외 개체의 속성은 블록 내에서 catch 액세스할 수 있습니다. 예를 들어 이 속성을 사용하여 Message 애플리케이션 사용자에게 문제를 알릴 수 있습니다.
  • 둘 이상의 예외 형식을 catch해야 하는 경우, catch 절(구문)을 두 개 이상 지정할 수 있습니다.