C#의 예외 처리 구문은 try-catch-finally 부분과 throw 부분으로 나눌 수 있습니다.
try 블록은 예외가 발생할 수 있는 코드를 전개하는 부분이고 catch 블록은 예외가 발생했을 때 처리하는 부분입니다. 그리고 finally 블록은 선택적으로 사용할 수 있는데 프로그램 흐름이 try 블록이나 catch 블록을 마칠 때 공통적으로 처리해야 하는 루틴을 작성합니다. 마지막으로 throw는 예외를 감지했을 때 예외를 catch 블록에 전달하는 역할을 합니다. 만약 예외를 던졌는데 try catch 블록이 없으면 프로그램은 예외 발생 문구와 함께 종료합니다.
간단한 예를 들어볼게요. 정수를 보관하는 클래스 Demo를 만듭시다. Demo클래스는 생성자에서 배열의 크기를 전달받습니다. 그리고 인덱서의 get 블록에서는 유효한 인덱스일 때 해당 원소에 값을 반환하고 set블록에서는 유효한 인덱스일 때 해당 원소에 값을 설정합니다. 그런데 유효하지 않은 인덱스일 때 어떻게 해야 할까요? 이럴 때 예외를 던지는 것입니다.
class Demo { int[] arr = null; internal int this[int index] { get { if ((index < 0) || (index >= arr.Length)) { throw new Exception("index가 유효하지 않습니다."); } return arr[index]; } set { if ((index < 0) || (index >= arr.Length)) { throw new Exception("index가 유효하지 않습니다."); } arr[index] = value; } } internal Demo(int capacity) { arr = new int[capacity]; } }
사용하는 곳에서 try catch 블록을 두지 않고 잘못 사용하여 예외가 발생하면 프로그램은 예외 구문을 출력하고 프로그램은 종료합니다.
class Program { static void Main(string[] args) { Demo demo = new Demo(10); demo[2] = 4; demo[10] = 8; //유효하지 않은 인덱스 사용 } }
만약 try 블록 내부에서 Demo 클래스를 사용하고 catch 블록에서 예외를 처리하면 예외를 던지면 catch 블록을 수행합니다.
static void Main(string[] args) { try { Demo demo = new Demo(10); demo[2] = 4; demo[10] = 8; //유효하지 않은 인덱스 사용 } catch (Exception e) { Console.WriteLine("예외 발생:{0}", e.Message); } Console.WriteLine("Hello World"); }
만약 try 블록에서 파일을 열고 작업을 수행하면 예외가 발생하거나 발생하지 않아도 작업 중인 파일을 닫아야 할 것입니다. 이처럼 예외 발생에 관계없이 반드시 수행해야 하는 구문이 있으면 선택적으로 fianlly 블록을 둘 수 있습니다. finally 블록의 코드는 try 블록이나 catch 블록 중간에 return문을 사용해도 수행함을 보장합니다.
static void Main(string[] args) { FileStream fs = new FileStream("Demo.txt", FileMode.Create); try { Demo demo = new Demo(10); demo[2] = 4; demo[10] = 8; //유효하지 않은 인덱스 사용 } catch (Exception e) { Console.WriteLine("예외 발생:{0}", e.Message); } finally { Console.WriteLine("finally 블록 수행"); fs.Close(); } }
▶ 실행 결과
예외 발생: index가 유효하지 않습니다. finally 블록 수행