예외상황이 생겼을때, CLR은 스스로 에러메세지를 출력하고, 프로그램은 죽어버린다. 따라서 그 뒤에있는 코드들은 실행되지도않은채 프로그램이 종료되게된다. 이렇게 예외를 잘 처리하지못하고 죽어버리는 프로그램은 신뢰할 수 없다. 따라서 프로그래머는 예외가 자신이 작성한 코드 내에서 처리되도록 조치를 취해야한다.
try~ catch로 예외받기
프로그램에서 Main() 메소드에 예외를 던졌을때, Main()메소드가 예외를 받지 못해서 프로그램이 죽는것이기 때문에, Main()메소드가 예외를 받으면 이 문제를 해결할 수 있다. C# 에서는 try ~ catch 구문으로 이 문제를 해결한다.
try
{
//실행할 코드
}
catch( 예외객체1)
{
//예외가 발생했을때의 처리
}
catch( 예외객체2)
{
//예외가 발생했을때의 처리
}
try절에는 예외가 일어나지 않았을경우 실행되어야 할 코드가, catch에는 예외가 발생했을때의 처리코드가 들어간다. 이때 catch 절은 try 블록에서 던질 예외 객체와 형식이 같아야 한다.
ex)
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("{0}",arr[i]);
}
catch( IndexOutOfRangeException e)
{
Console.WriteLine("예외가 발생했습니다 : {0}",e.Message);
//예외시 프로그램은 IndexOutOfRangeException 자료형의 예외를 던지고 이는 객체 e 의 Message안에 예외 정보를 담아 저장된다,
}
Console.WriteLine("종료");
}
}
}
System.Exception 클래스
C# 에서 모든 예외 클래스는 반드시 System.Exception 클래스로부터 상속받아야한다. IndexOutOfRangeException 예외 또한 이 클래스로부터 파생된 것이다. 따라서 상속관계로 인해 모든 예외 클래스들은 System.Exception 형식으로 간주할 수 있고, System.Excecption 형식의 예외를 받는 catch 절 하나면 모든 예외를 다 받을 수 있다.
ex)
try
{
for(int i = 0; i<5; i++)
Console.WriteLine("{0}",int[i]);
}
catch( Exception e )
{
}
예외상황에 따라 섬세한 예외 처리가 필요한 경우 , Exception 클래스를 남용해선 안된다. Exception 형식은 프로그래머가 발생할 것으로 계산한 예외 말고도 다른 예외까지 받아낼 수 있다. 그 예외가 현재 코드가아닌 상위 코드에서 처리해야할 예외라면 , 이 코드는 버그를 만들고 있는 셈이 되므로, 남용해선안된다.
예외 던지기
예외를 던질때는 throw문을 이용해서 던진다
try
{
//
throw new Exception(" 예외를 던집니다");
}
catch(Exception e)
{
Console.WriteLine("e.Message);
}
ex)
static void DoSomething ( int arg)
{
if( arg<10)
Console.WriteLine("arg : {0}" , arg);
else
throw new Exception("arg가 10보다 큽니다"); // 예외를 던져도 예외를 받을 코드가 이 메소드 안에 없기 때문에, 예외는 DoSometiong 메소드의 호출자에게 던져진다.
}
static void Main()
{
try
{
Dosomething(14);
}
catch( Exception e)
{
Console.WriteLine("{0}",e.Message);
}
}
try~catch 와 finally
try 블록을 실행하다가 예외가 던져지면 프로그램이 catch 절로 바로 넘어가버린다. 그로인해 try 블록 안에있는 자원해제같은 중요한 코드를 미처 실행하지 못한다면 이는 곧 버그의 원인이 된다. 예를들어, 데이터베이스의 커넥션을 닫는 코드가 있는데 , 예외때문에 이 코드가 실행되지 못한다면, 사용할 수 있는 커넥션이 점점 줄어 나중에는 데이터베이스에 연결할 수 없는 상태가 된다. 이때 finally 절을 try ~cathc 문 가장 마지막에 연결하여 사용하면, 자신이 소속되어있는 try 절이 실행된다면 finally 절은 어떤경우라도 실행된다. 심지어 try 절안에 return 혹은 throw 문이 사용되더라도 finally 문은 꼭 실행된다.
try
{
dbconn.Open; // dbconn은 데이터베이스 커넥션
//
}
catch (XXXException e)
{
}
catch(YYYException e)
{
}
finally
{
dbconn.Close();
}
ex)
static int Divide(int divisor , int dividend)
{
try
{
Console.WriteLine("Divide() 시작");
return divisor / dividend;
}
catch(DivideByZeroException e)
{
Console.WriteLine("Divide() 예외 발생");
throw e;
}
finally
{
Console.WriteLine("Divided() 끝");
}
}
사용자 정의 예외 클래스 만들기
모든 예외 클래스는 System.Exception 클래스로부터 파생되어야 한다. 이 규칙에 의거해서, Exception 클래스를 상속하기만 한다면, 새로운 예외클래스를 정의할 수 있다.
class MyException : Exception
{
//
}
.NET 프레임 워크에서 100여가지가 넘는 예외형식을 제공하기 때문에 사용자 정의 예외클래스는 자주 사용하지는 않지만, .NET 프레임 워크에서 지원하지 않는 예외형식이거나, 예외상황을 더 잘 설명하고싶을때 이용한다.
ex)
namespace MyException
{
class InvalidArgumentException : Exception
{
public InvaildArgumentException() // 매개변수 없는 생성자
{
}
public InvalidArgumentException(string message) : Base(message) // 매개변수가 있는생성자, 기반클래스 ( Exception ) 에 매개변수 전달
{
}
pubilc object Argument
{
get;
set;
} // 프로퍼티
}
}
예외처리 다시 생각해보기
try~ catch 문을 이용한 예외 처리는 실제 일을 하는 코드와 문제를 처리하는 코드를 깔끔하게 분리시킴으로서 코드를 간결하게 만들어준다.
이 뿐만 아니라 예외객체의 StackTrace 프로퍼티를 통해 문제가 발생한 부분의 소스코드 위치를 알려주기때문에 디버깅이 아주 용이하다.
ex)
using System
namespace StackTrace
{
class MainApp
{
static void Main(string args[])
{
try
{
int a =1;
Console.WriteLine(3/-a);
}
catch (DivideByZeroException e)
{
Console.WriteLine(e.StackTrace); // 위치 : StackTrace.MainApp.Main(String[] args) 파일 : D:\stackTrace\MainApp.cs : 줄12 출력
}
}
}
}
또한, 예외처리는 여러 문제점을 하나로 묶어내거나 코드에서 발생할 수 있는 오류를 종류별로 정리해주는 효과가 있다. 예를들어 try 블록의 코드에서 DivideByZeroException예외를 일으킬 수 있는 부분은 둘 이상일 수 있지만 , 이 형식을 받는 Catch 블록 하나면 모두 처리가 가능하다.
'C#언어 > 뇌자극C#5.0' 카테고리의 다른 글
C# : Chapter - 12 델리게이트와 이벤트 (0) | 2018.01.28 |
---|---|
C# : Chapter -10 일반화 프로그래밍 (0) | 2018.01.14 |
C# : Chapter -9 배열 , 컬렉션 , 인덱서 (0) | 2018.01.12 |
C# : Chapter -8 프로퍼티 (0) | 2018.01.08 |
C# : Chapter -7 인터페이스 (0) | 2017.12.16 |