[Effective C#] 45. 메서드가 실패했음을 알리기 위해서 예외를 이용하라

1. 참조



2. 소개

  • 메서드가 요청된 작업을 제대로 수행할 수 없는 경우 예외를 발생시켜 실패가 발생했음을 알려야 합니다.


3. 반환 코드

  • 반환 코드의 경우 메서드의 원형에 영향을 미칩니다.
  • 계산의 결과를 나타내는 용도로 주로 사용되므로 오류가 발생했다는 사실 이외의 추가적인 정보를 전달하기가 어렵습니다.
using System;

namespace interfaceTest
{
    public enum FOO_RESULT
    {
        OK = 0,
        ERROR = 1,
        FOO = 2,
        BAR = 3
    }

    class Program
    {
        static void Main(string[] args)
        {
            for(int index = 0; index < 10; index++)
            {
                Console.WriteLine($"{DoSomeEnum(index)}");
            }
        }

        public static FOO_RESULT DoSomeEnum(int number)
        {
            /* 임의의 작업 */
            if (number == 5)
            {
                return FOO_RESULT.ERROR;
            }
            /* 임의의 작업 */
            if (number == 4)
            {
                return FOO_RESULT.FOO;
            }
            /* 임의의 작업 */
            if (number == 3)
            {
                return FOO_RESULT.FOO;
            }

            /* 임의의 작업 */
            return FOO_RESULT.OK;
        }
    }
}
OK
OK
OK
FOO
FOO
ERROR
OK
OK
OK
OK


4. 예외

  • 예외를 이용하는 방식은 클래스 타입이므로 개발자가 자신만의 예외 타입을 파생시킬 수 있으며 오류에 대한 추가적인 정보를 전달할 수 있습니다.
  • 콜 스택을 통해서 적절한 catch문이 구성된 위치까지 전파됩니다.
  • 적절한 catch문이 포함되어있지 않다면 응용프로그램이 종료되니 예외는 쉽게 무시하기 어렵습니다.
using System;

namespace Chapter45
{
    class Program
    {
        static void ProcessString(string s)
        {
            if (s == null)
            {
                throw new ArgumentNullException(paramName: nameof(s), message: "Parameter can't be null");
            }
        }

        static void Main(string[] args)
        {
            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);
            }
        }
    }
}
System.ArgumentNullException: Parameter can't be null (Parameter 's')
   at Chapter45.Program.ProcessString(String s) in D:\withcommunity\withcommunity\src\effectivecsharp3\Chapter45\Chapter45\Chapter45\Program.cs:line 11
   at Chapter45.Program.Main(String[] args) in D:\withcommunity\withcommunity\src\effectivecsharp3\Chapter45\Chapter45\Chapter45\Program.cs:line 20 First exception caught.


5. 사전에 예외 검사하기

  • 예외를 처리하는 작업은 일반적인 메서드 호출보다 시간이 많이 걸립니다.
  • 특정 작업을 수행하기 전에 실패할 가능성이 있는지 확인하는 편이 좋습니다.


5.1 잘못 사용된 예외처리

// 이런 방법은 사용하지 말라.
DoesWorkThatMightFail worker = new DoesWorkThatMightFail();
try
{
    worker.DoWork();
}
catch(WorkerException e)
{
    ReportErrorToUser("테스트 실패");
}


5.2 권장하는 예외처리

using System;

namespace Chapter45
{
    class Program
    {
        static void Main(string[] args)
        {
            string name = "유재석";
            TryDoWork(name);

            Console.WriteLine();

            string name2 = string.Empty;
            TryDoWork(name2);
        }

        public static bool TryDoWork(string name)
        {
            if (!CheckNameConditions(name))
                return false;

            Work(); // 여전히 실패할 가능성은 있지만 가능성은 낮다.
            return true;
        }

        public static bool CheckNameConditions(string name)
        {
            if (name == null || name.Length <= 0)
            {
                Console.WriteLine("이름 형식에 문제 있음!!");
                return false;
            }

            return true;
        }

        public static void Work()
        {
            Console.WriteLine("이름 정합성 검증 성공!!");
        }
    }
}
이름 정합성 검증 성공!!

이름 형식에 문제 있음!!
  • 위와 같이 코드를 작성하면 예외가 발생할 가능성이 있는 메서드를 호출하기 이전에 사전 조건을 테스트해볼 수 있으므로 예외로 인한 성능 저하를 피할 수 있습니다.


6. 정리

  • 메서드가 실패했을 때는 언제나 예외를 발생하는 것이 좋지만, 일반적인 흐름 제어 메커니즘으로 사용해서는 안됩니다.
  • 또한, 사전에 테스트할 수 있는 메서드를 같이 제공하는 것이 바람직합니다.
728x90

이 글을 공유하기

댓글

Designed by JB FACTORY