C# ) C# 기초 4 (일반화 프로그래밍, 예외처리)
일반화 프로그래밍
특수한 개념으로부터 공통된 개념을 찾아 묶는 것을 ‘일반화.Generalization’라고 합니다.
일반화 프로그래밍GenericProgramming은 이러한 일반화를 이용하는 프로그래밍 기법 입니다. 일반화 프로그래밍은 일반화하는 대상이 조금 특이한데, 바로 '데이터 형식Data Type'입니다.
** 오버로딩을 하지 않고도 모든 형식을 지원하는 프로그래밍을 뜻한다.
일반화 메서드
한정자 반환_형식 메서드_이름<형식_매개변수> (매개변수_목록)
{
//...
}
사용예시
// int버전
void CopyArray(int[] source, int[] target)
{
for(int i = 0; i< source.Length; i++)
{
target[i] = source[i];
}
}
// String 버전
void CopyArray(string[] source, string[] target)
{
for(int i = 0; i< source.Length; i++)
{
target[i] = source[i];
}
}
// 일반화 메서드
void CopyArray(T[] source, T[] target)
{
for(int i = 0; i< source.Length; i++)
{
target[i] = source[i];
}
}
데이터 형식이 사용된 부분을 T 기호로 치환하여 사용 / T는 '형식 Type'을 뜻합니다.
" T가 C#에서 지원하는 형식이 아니여서 컴파일 에러가 발생하지만 T가 구체적으로 어떤 형식인지 지정을 해줘야 합니다."
void CopyArray<T>(T[] source, T[] target)
{
for(int i = 0; i< source.Length; i++)
{
target[i] = source[i];
}
}
** 형식 매개변수로 입력하는 방법 : 메서드 이름 뒤에 <와>를 넣어주고 그사이에 T를 넣으면 T는 '형식 매개변수 Type Parameter'가 됩니다.
CopyArray()를 호출할때 <> 사이에 T대신 형식으이 이름을 입력하면 컴파일러는 메서드의 나머지 부분에 대해서도 T를 형식 매개변수 값으로 치환합니다.
using System;
namespace CopyingArray
{
class MainApp
{
static void CopyArray<T>(T[] source, T[] target)
{
for (int i = 0; i < source.Length; i++)
target[i] = source[i];
}
static void Main(string[] args)
{
int[] source = { 1, 2, 3, 4, 5 };
int[] target = new int[source.Length];
CopyArray<int>(source, target);
foreach (int element in target)
Console.WriteLine(element);
string[] source2 = { "하나", "둘", "셋", "넷", "다섯" };
string[] target2 = new string[source2.Length];
CopyArray<string>(source2, target2);
foreach (string element in target2)
Console.WriteLine(element);
}
}
}
일반화 클래스
일반화 클래스는 (데이터 형식을) 일반화한 클래스입니다. 일반화 클래스를 선 언하는문법은다음과같습니다. 일반화메소드가그랬던 것처럼, 일반화클래스도형식 매개변수가 있는 것을 제외하면 보통의 클래스와 똑같습니다.
class 클래스_이름 < 형식_매개변수>
{
// ...
}
사용 예시
// int 형식
class Array_Int
{
private int[] array;
//...
public int GetElement(int index) {return array [index];}
}
// double 형식
class Array_double
{
private double[] array;
//...
public double GetElement(int index) {return array [index];}
}
// 일반화 클래스
class Array_Generic<T>
{
private T[] array;
//...
public T GetElement(int index) {return array [index];}
}
[ 일반화 클래스 사용할때 ]
Array_Generic<int> intArr = new Array_Generic<int>();
Array_Generic<double> dblArr = new Array_Generic<double>();
using System;
namespace Generic
{
class MyList<T>
{
private T[] array;
public MyList()
{
array = new T[3];
}
public T this[int index]
{
get
{
return array[index];
}
set
{
if (index >= array.Length)
{
Array.Resize<T>(ref array, index + 1);
Console.WriteLine($"Array Resized : {array.Length}");
}
array[index] = value;
}
}
public int Length
{
get { return array.Length; }
}
}
class MainApp
{
static void Main(string[] args)
{
MyList<string> str_list = new MyList<string>();
str_list[0] = "abc";
str_list[1] = "def";
str_list[2] = "ghi";
str_list[3] = "jkl";
str_list[4] = "mno";
for (int i = 0; i < str_list.Length; i++)
Console.WriteLine(str_list[i]);
Console.WriteLine();
MyList<int> int_list = new MyList<int>();
int_list[0] = 0;
int_list[1] = 1;
int_list[2] = 2;
int_list[3] = 3;
int_list[4] = 4;
for (int i = 0; i < int_list.Length; i++)
Console.WriteLine(int_list[i]);
}
}
}
형식 매개변수 제약
일반화 메소드나 일반화 클래스가 입력받는 형식 매개변수 T는 ‘모든’ 데이터 형식을 대신할 수 있었 습니다. 이렇게 모든형식에 대응할수 있는형식 매개변수가필요한때도 있지만, 종종특정 조건을 갖춘 형식에만 대웅하는 형식 매개변수가 필요할 때도 있습니다. 이때 우리는 형식 매개변수의 조건 에제약을줄수있습니다.
class MyList<T> where T : MyClass
{
//..
}
CopyArray()의 형식 매개변수 T에 ‘값 형식이어야 할 것’ 이라는 재약은 디음과 같이 줄 수 있습니다.
void CopyArray<T>(T[] source, T[] target) where T : struct
{
for( int i = 0; i < source.Length; i++)
{
target[i] = source[i];
}
}
// 형식 매개변수
where 형식_매개변수 : 제약조건
일반화 컬렉션
. 일반화 컬렉 션은 말 그대로 일반화에 기반해서 만들어져 있기 때문에 컴파일할 때 컬렉션에서 사용할 형식이 결 정되고, 쓸데없는 형식 변환을 일으키지 않습니다. 또한 잘못된 형식의 객체를 담게 될 위험도 피할 수있습니다. System.Collections.Generic 네임스페이스는 다양한 컬렉션 클래스를 담고 있지만, 지면의 한계 상 대표적인 다음 네 가지 클래스만 다루려 합니다.
• List<T>
• Queue<T>
• Stack<T>
• Dictionary<TKey, TValue>
foreach를 사용할 수 있는 일반화 클래스
일반화 클래스도 IEnumerable 인터페이스를 상속하면 일단은 foreach를 통해 순회할 수 있지만, 요소를 순회할 때마다 형식 변환을 수행하는 오버로드가 발생한다는 문제가 있습니다.
System.Collections.Generic 네임스페이스에 이 문제를 풀 수 있는 열쇠가 있습니다. IEnumerable 의 일반화 버전인 IEnumerable 인터페이스가 바로 그것입니다. 이 인터페이스 를 상속하면 형식 변환으로 인한 성능 저하가 없으면서도 foreach 순회가 가능한 클래스를 작성할 수있습니다.
[ IEnumerable 인터페이스의 메소드 ]
메서드 | 설명 |
IEnumrator GetEnumerator() | IEnumerator 형식의 객체를 반환(IEnumerable로 부터 상속받은 메서드) |
IEnumerator<T> GetEnumerator() | IEnumerator<T>형식의 객체를 반환 |
[ IEnumerator <T>의 메서드와 프로퍼티 ]
메서드 또는 프로퍼티 | 설명 |
boolean MoveNext() | 다음 요소로 이동합니다. 컬렉션의 끝을 지난 경우에는 false, 이동이 성공한 경우에는 true를 반환합니다. |
void Reset() | 컬렉션의 첫 번째 위치의 '앞'으로 이동합니다. 첫 번째 위치가 0번일 때, Reset()을 호출하면 -1번으로 이동하는 것이죠 . 첫 번째 위치로의 이동은 MoveNext()를 호출한 다음에 이루어집니다. |
Object Current{get;} | 컬렉션의 현재 요소를 반환합니다. (IEnumerator로부터 상속받은 프로퍼티). |
T Current{get;} | 컬렉션의 현재요소를 반환합니다. |
예외처리
try
{
// 실행하려는 코드
}
catch(예외_객체_1)
{
// 예외가 발생했을 때의 처리
}
catch(예외_객체_2)
{
// 예외가 발생했을 때의 처리
}
* System.Exception 클래스 : 모든 예외의 조상 , C#에서 모든 예외 클래스는 반드시 이 클래스로부터 상속받아야 합니다.
예외 던지기
* throw : try ~ catch문으로 예외를 받는다는것은 어디선가 예외를 던진다는 이야기입니다. 예외 던지는 키워드로 Throw가 사용됩니다.
try
{
//...
throw new Exception("예외를 던집니다."); // throw 문을 통해 던져진 예외 객체는 Catch문을 통해 받습니다.
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
사용예시
static void DoSomething(int arg)
{
if(arg < 10)
{
Console.WriteLine("arg : {0}", arg);
}
else
{
throw new Exception("arg가 10보다 큽니다.");
// 예외를 던졌지만 DoSomething() 메서드안에서는 이 예외를 처리할 수 있는 코드가 없습니다.
// 이 예외는 DoSomething() 메서드의 호출자에게 던져집니다.
}
}
static void Main()
{
try
{
DoSomething(13); // DoSomething() 메서드에서 던진 호출자의 try ~ catch블록에서 받습니다.
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
try ~ catch 와 finally
C#에서는 예외 처리를 할 때 자원 해제 같은 뒷 마무리를 우아하게 실행할 수 있도록 Finally 절을 try ~ catch 문과 함께 사용합니다.
try ~ catch 문의 마지막에 연결해서 사용한다.
try
{
dbconn.Open(); //dbconn은 데이터베이스 커넥션
//...
dbconn.Close();
}
catch(XXXException e)
{
dbconn.Close();
}
catch(YYYException e)
{
dbconn.Close();
}
// 자원 해제 코드를 각 catch절마다 반복?
// finally 절 사용
try
{
dbconn.Open(); // dbconn은 데이터베이스 커넥션
//...
}
catch(XXXException e)
{
}
catch(YYYException e)
{
}
finally
{
dbconn.Close();
}
try 절 안에서 return 문이나 throw 문이 사용되더라도(이 두 문장은 프로그램의 흐름 제어를 외부 코드로 옮깁 니다) finally 절은 꼭 실행됩니다.