C# 예외(Exception)란 무엇인가?
예외(Exception)는 프로그램 실행 도중에 발생하는 문제를 의미합니다. C#에서 예외란 프로그램이 실행되는 동안 발생하는 비정상적인 상황에 대한 대응을 말하며, 대표적인 예로 0으로 나누기를 시도하는 경우를 들 수 있습니다.
예외는 프로그램의 한 부분에서 다른 부분으로 제어 흐름을 전달하는 메커니즘을 제공합니다. C#의 예외 처리는 다음 네 가지 키워드를 기반으로 구성됩니다.
1. try
try 블록은 특정 예외가 감지될 수 있는 코드 영역을 지정합니다. try 블록 뒤에는 반드시 하나 이상의 catch 블록이 따라와야 합니다.
2. catch
catch는 프로그램 내에서 문제를 처리하고자 하는 지점에 예외 처리기(exception handler)를 배치하여 예외를 잡아내는 역할을 합니다. catch 키워드는 예외를 포착한다는 것을 나타냅니다.
3. finally
finally 블록은 예외가 발생하든 발생하지 않든 반드시 실행해야 하는 명령문들을 담는 데 사용됩니다. 예를 들어 파일을 열었다면, 예외 발생 여부와 관계없이 해당 파일을 반드시 닫아야 하므로 이러한 정리 작업에 finally 블록이 적합합니다.
4. throw
프로그램은 문제가 발생한 시점에 예외를 던지게 되며, 이는 throw 키워드를 사용해 수행됩니다.
C# 예외 클래스 계층 구조
C#에서 예외는 클래스로 표현됩니다. C#의 예외 클래스들은 대부분 System.Exception 클래스로부터 직접 또는 간접적으로 파생됩니다. System.Exception에서 파생된 대표적인 클래스는 다음과 같습니다.
- System.ApplicationException — 응용 프로그램에서 발생하는 예외를 위한 기본 클래스입니다.
- System.SystemException — 시스템 수준의 예외를 위한 기본 클래스입니다.
그 외에도 자주 접하게 되는 예외 클래스로는 0으로 나눌 때 발생하는 DivideByZeroException, null 인자가 전달될 때 발생하는 ArgumentNullException, null 참조에 접근할 때 발생하는 NullReferenceException 등이 있습니다.
예제 1: 리플렉션과 예외 처리
먼저 다음 예제를 살펴보겠습니다.
using System;
using System.Reflection;
public class Demo {
public static void Main(){
Type type = typeof(Subject);
try {
FieldInfo fieldInfo = type.GetField("SubName");
MemberInfo[] info = type.GetMember("SubName");
Console.Write("Members = ");
for (int i = 0; i < info.Length; i++)
Console.WriteLine(" {0}", info[i]);
Console.WriteLine("FieldInfo = {0}", fieldInfo);
}
catch (ArgumentNullException e){
Console.Write("{0}", e.GetType(), e.Message);
}
}
}
public class Subject{
public string SubName = "Science";
}출력 결과
Members = System.String SubName FieldInfo = System.String SubName
위 예제는 Subject 클래스의 멤버 정보를 조회하며, 필드나 멤버 이름으로 null이 전달될 경우 발생할 수 있는 ArgumentNullException을 catch 블록에서 처리하도록 작성되어 있습니다.
예제 2: 0으로 나누기 예외 처리
이번에는 또 다른 예제를 살펴보겠습니다.
using System;
public class Demo {
int result;
Demo() {
result = 0;
}
public void division(int num1, int num2) {
try {
result = num1 / num2;
}
catch (DivideByZeroException e) {
Console.WriteLine("Exception caught = {0}", e);
}
finally {
Console.WriteLine("Result = {0}", result);
}
}
public static void Main(string[] args) {
Demo d = new Demo();
d.division(100, 0);
}
}출력 결과
Exception caught = System.DivideByZeroException: Attempted to divide by zero.
at Demo.division(Int32 num1, Int32 num2) in d:\Windows\Temp\n0kebv45.0.cs:line 11
Result = 0위 예제에서 division 메서드는 num1을 num2로 나누려고 시도합니다. num2가 0이므로 DivideByZeroException이 발생하고, catch 블록이 이를 포착하여 콘솔에 출력합니다. 마지막으로 finally 블록은 예외 발생 여부와 관계없이 항상 실행되어 결과값을 화면에 출력합니다.
마무리
예외 처리는 안정적인 프로그램을 작성하기 위한 필수 요소입니다. try-catch-finally 패턴과 throw 키워드를 적절히 활용하면 런타임 오류로부터 프로그램을 보호하고, 예측 가능한 방식으로 오류 상황에 대응할 수 있습니다.