일반화 프로그래밍
특수한 개념으로부터 공통된 개념을 찾아 묶는 것을 ‘일반화.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 절은 꼭 실행됩니다.
'LAB > C#' 카테고리의 다른 글
C# ) C# 기초 (스레드 , 태스크 ,네트워크 프로그래밍) (0) | 2024.11.02 |
---|---|
C# ) C# 기초 5 (대리자, 이벤트) (0) | 2024.11.01 |
C# ) C# 기초 3 (프로퍼티, 컬렉션, 인덱서) (0) | 2024.10.31 |
C# ) C# 기초 2 (구조체 , 튜플, 인터페이스, 추상클래스) (0) | 2024.10.30 |
C#) C# 기초 (데이터 형식 , 자료구조, 클래스) (0) | 2024.10.30 |