[이것이 C#이다] 12. 예외 처리하기

ssu_hyun·2022년 5월 3일
0

C#

목록 보기
16/22

Key point

  • 예외
  • try~catch~finally
  • System.Exception 클래스 & 파생 클래스
  • throw문
  • 예외 필터



12.1 예외(Exception)

프로그래머가 생각하는 시나리오에서 벗어나는 사건

예외처리(Exception Handling)

예외가 프로그램의 오류나 다운으로 이루어지지 않도록 적절하게 처리하는 것

using System;

namespace KillingProgram
{
    class MainApp
    {
        static void Main(string[] args)
        {
            int[] arr = {1, 2, 3};

            for (int i = 0; i < 5; i++) // IndexOutOfRangeException => 프로그램 강제 종료
            {
                Console.WriteLine(arr[i]);
            }

            Console.WriteLine("종료");
        }
    }
}

12.2 try~catch

  • 형식
try
{
	// 실행하고자 하는 코드
}
catch( 예외_객체_1 )  // try블록에서 던질 예외 객체와 형식 일치
{
	// 예외가 발생했을 때의 처리
}
catch( 예외_객체_2 )
{
	// 예외가 발생했을 때의 처리
}
using System;

namespace TryCatch
{
    class MainApp
    {
        static void Main(string[] args)
        {
            int[] arr = { 1, 2, 3 };

            try
            {
                for (int i = 0; i < 5; i++)
                {
                    Console.WriteLine(arr[i]);  // i=3 -> IndexOutOfRangeException 예외 발생
                }
            }
            catch( IndexOutOfRangeException e ) // catch 블록이 위 예외 받아냄
            {
                Console.WriteLine($"예외가 발생했습니다 : {e.Message}");
            }

            Console.WriteLine("종료");
        }
    }
}



12.3 System.Exeption 클래스

  • 모든 예외의 조상으로 C#의 모든 예외 클래스는 반드시 이 클래스로부터 상속받는다.
// 다음 2개의 예외 코드는
try
{
}
catch( IndexOutOfRangeException e )
{
	//...
}
catch( DivideByZeroException e )
{
	//...
}

// 아래의 하나의 catch절로 처리할 수 있다.
try
{
}
catch( Exception e )
{
	//...
}
  • 그러나 상황에 따라 섬세한 예외 처리가 필요할 경우 Exception 클래스만으로 대응이 어려우므로 무조건 Exception 클래스를 사용하는 것은 금물이다.
  • 코드를 면밀히 검토해 처리하지 않아야 할 예외까지 처리하는 것은 아닌지 확인할 것



12.4 예외 던지기

  • 예외는 throw문을 이용해 던지고 catch문을 통해 받는다.
using System;

namespace Throw
{
    class MainApp
    {
        static void DoSomething(int arg)
        {
            if (arg < 10)
                Console.WriteLine($"arg : {arg}");
            else
                throw new Exception("arg가 10보다 큽니다.");
        }
        static void Main(string[] args)
        {
            try
            {
                DoSomething(1);
                DoSomething(3);
                DoSomething(5);
                DoSomething(9);
                DoSomething(11);  // 예외 발생
                DoSomething(13);  // 위 예외 발생으로 인해 코드 실행X
            }
            catch (Exception e)  // 위 예외 발생 함수의 throw문이 던진 예외 객체를 catch가 받아냄
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}
  • C# 7.0부터는 throw를 식(expression)으로도 사용할 수 있다.
using System;

namespace ThrowExpression
{
    class MainApp
    {
        static void Main(string[] args)
        {
            try
            {
                int? a = null;
                int b = a ?? throw new ArgumentNullException();
            }
            catch (ArgumentNullException e)
            {
                Console.WriteLine(e);
            }

            try
            {
                int[] array = new[] { 1, 2, 3 };
                int index = 4;
                int value = array[
                    index >= 0 && index < 3 
                    ? index : throw new IndexOutOfRangeException()
                    ];
            }
            catch (IndexOutOfRangeException e)
            {
                Console.WriteLine(e);
            }
        }
    }
}



12.5 finally

  • try~catch문의 제일 마지막에 연결해 사용하여 어떤 경우라도 반드시 실행되도록 하는 절
using System;

namespace Finally
{
    class MainApp
    {
        static int Divide(int divisor, int dividend)
        {
            try
            {
                Console.WriteLine("Divide() 시작");
                return divisor / dividend;
            }
            catch (DivideByZeroException e)
            {
                Console.WriteLine("Divide() 예외 발생");
                throw e;
            }
            finally  // 위에서 return하든지 throw를 하든지 무조건 실행하는 절 
            {
                Console.WriteLine("Divide()  끝");
            }
        }

        static void Main(string[] args)
        {
            try
            {
                Console.Write("제수를 입력하세요. :");
                String temp = Console.ReadLine();
                int divisor = Convert.ToInt32(temp);

                Console.Write("피제수를 입력하세요. : ");
                temp = Console.ReadLine();
                int dividend = Convert.ToInt32(temp);

                Console.WriteLine("{0}/{1} = {2}",
                    divisor, dividend, Divide(divisor, dividend));
            }
            catch (FormatException e)
            {
                Console.WriteLine("에러 : " + e.Message);
            }
            catch (DivideByZeroException e)
            {
                Console.WriteLine("에러 : " + e.Message);
            }
            finally  // 무조건 실행
            {
                Console.WriteLine("프로그램을 종료합니다.");
            }
        }
    }
}



12.6 사용자 정의 예외 클래스 만들기

using System;

namespace MyException
{
    class InvalidArgumentException : Exception  // Exception 상속
    {
        public InvalidArgumentException()
        {
        }

        public InvalidArgumentException(string message)
            : base(message)
        {
        }

        public object Argument
        {
            get;
            set;
        }

        public string Range
        {
            get;
            set;
        }
    }

    class MainApp
    {
        static uint MergeARGB(uint alpha, uint red, uint green, uint blue)
        {
            uint[] args = new uint[] { alpha, red, green, blue };

            foreach (uint arg in args)
            {
                if (arg > 255)
                    throw new InvalidArgumentException()
                    {
                        Argument = arg,
                        Range = "0~255"
                    };
            }

            return (alpha << 24 & 0xFF000000) |
                   (red   << 16 & 0x00FF0000) |
                   (green << 8  & 0x0000FF00) |
                   (blue        & 0x000000FF);
        }

        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("0x{0:X}", MergeARGB(255, 111, 111, 111));
                Console.WriteLine("0x{0:X}", MergeARGB(1, 65, 192, 128));
                Console.WriteLine("0x{0:X}", MergeARGB(0, 255, 255, 300));
            }
            catch (InvalidArgumentException e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine($"Argument:{e.Argument}, Range:{e.Range}");

            }
            
        }
    }
}


12.7 예외 필터(Exception Filter)

  • 예외 객체 속 제약 사항의 조건을 만족하는 예외 객체에 대해서만 예외 처리 코드 실행 (C#6.0~)
  • catch()절 뒤에 when 키워드 이용
using System;

namespace ExceptionFiltering
{
    class FilterableException : Exception
    {
        public int ErrorNo {get;set;}
    }

    class MainApp
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Enter Number Between 0~10");
            string input = Console.ReadLine();
            try
            {
                int num = Int32.Parse(input);

                if (num < 0 || num > 10)
                    throw new FilterableException() { ErrorNo = num };
                else
                    Console.WriteLine($"Output : {num}");
            }
            catch (FilterableException e) when (e.ErrorNo < 0)  // 예외 객체의 ErrorNo가 0보다 작을 경우 catch
            {
                Console.WriteLine("Negative input is not allowed.");
            }
            catch(FilterableException e) when (e.ErrorNo > 10)  // 예외 객체의 ErrorNo가 10보다 클 경우 catch
            {
                Console.WriteLine("Too big number is not allowed.");
            }
        }
    }
}



연습 문제

  1. 다음 코드를 컴파일하면 실행 결과처럼 예외를 표시하고 비정상 종료합니다. try~catch문을 이용해서 예외를 안전하게 처리하도록 코드를 수정하세요.
using System;

namespace Ex12_1
{
    class MainApp
    {
        static void Main(string[] args)
        {
            int[] arr = new int[10];
            
            for(int i=0; i<10; i++)
                    arr[i] = i;
            try
            {
                for(int i=0; i<11; i++)
                    Console.WriteLine(arr[i]);
            }
            catch (IndexOutOfRangeException e)
            {
                Console.WriteLine(e)
            }
        }
    }
}


0개의 댓글