C#의 일반화 프로그래밍 Generic Programming
C++에서 template<typename T>를 C#에서 어떻게 하는 지 알아본다.
일반화 메서드
메서드 이름 뒤에 <T>를 붙여 임의의 타입에 대해 동작하는 일반화 메서드를 정의한다.
void CopyArray<T> (T[] source, T[] target)
{
for(int i =0;i<source.Length; ++i)
target[i] = source[i];
}
일반화 클래스
클래스도 마찬가지로 클래스 이름 뒤에 <T>를 붙인다.
class Container<ElementType>
{
private ElementType[] array;
public Container()
{
}
public ElementType this[int index]
{
get { return array[index]; }
set { array[index] = value; }
}
public int Length { get { return array.Length; } }
}
C#에서는 where 절을 이용하여 해당 클래스의 타입 T에 대해 제약을 추가할 수 있다.
제약조건 | 설명 |
where T : struct | value type 이면서 non-nullable 인자를 사용 가능하다. struct 타입은 new()가 가능하기 때문에 new 제약사항과 혼용할 수 없다. 또한 unmanaged 제약사항을 쓸 수 없다. |
where T : class | reference type이면서 non-nullable 인자를 사용가능하다. 이 제약사항은 클래스, 인터페이스, 델리게이트, 혹은 배열타임이 가능하다. |
where T : class? | reference type과 nullable 인자를 사용가능하다. |
where T : notnull | non-nullable 인자만 사용가능하다. |
where T : default | C# 9.0이상에서 이 제약사항은 기반 메서드를 오버라이드할 때 제약사항이 없는 메서드를 오버라이드하겠다는 의미를 지닌다. |
where T : unmanaged | non-nullable unmanaged type 인자로 제약한다. 이 제약은 struct를 암시하기 때문에 struct와 new()를 같이 사용할 수 없다. |
where T : new() | 매개변수가 없는 public new()를 호출할 수 있어야한다. struct와 unmanaged와 같이 사용할 수 없다. |
where T : base_class_name | T가 기반 클래스가 base_class_name 이어야한다. |
where T : interface_name | T가 interface_name을 상속한 클래스이어야한다. |
where T : U | T는 또다른 인자 U 클래스로 부터 상속한 클래스이어야한다. |
간단한 예시
class StructArray<T> where T : struct
{
public T[] Array { get; set; }
public StructArray(int size) { Array = new T[size]; }
}
class RefArray<T> where T : class {
public T[] Array { get; set; }
public RefArray(int size) { Array = new T[size]; }
}
class ObjectBase { }
class Object : ObjectBase { }
class BaseArray<U> where U : ObjectBase
{
public U[] Array { get; set; }
public BaseArray(int size) { Array = new U[size]; }
public void CopyArray<T>(T[] Source) where T : U
{
Source.CopyTo(Array, 0);
}
}
예외처리하기
try-catch-finally 구문을 사용한다.
- try 절 안에서 예외가 발생하면 catch 문 중 해당 예외에 대한 처리를 수행할 수 있다. 해당 예외가 없다면 프로그램은 멈춘다.
- 모든 Exception은 System.Exception 클래스를 상속한다.
static void Main(string[] args)
{
int[] arr = { 1, 2, 3 };
try
{
for (int i = 0; i < 5; ++i)
{
Console.WriteLine(arr[i]);
}
}
catch (IndexOutOfRangeException e)
{
Console.WriteLine($"예외가 발생했습니다. : { e.Message}");
}
try
{
throw new Exception("예외를 던진다.");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
C# 6.0에서 부터 when 절을 이용하여 예외를 선택적으로 처리할 수 있다.
class FilterableException : Exception
{ public int ErroNo { get; set; } }
static void Main(string[] args)
{
try
{
int num = Int32.Parse(input);
if (num < 0 || num > 10)
throw new FilterableException() { ErroNo = num };
else
Console.WriteLine($"Output : {num}");
}
catch (FilterableException e) when (e.ErroNo < 0)
{
Console.WriteLine("Negative Input is not allowed");
}
catch (FilterableException e) when (e.ErroNo > 10)
{
Console.WriteLine("Too big number is not allowed.");
}
}
사용자 예외 클래스를 만들고 예외가 발생할 때 해당 예외에 대한 속성을 지정한다. catch 문에서는 when 절을 통해 해당 속성 값에 따라 다른 예외처리를 수행한다.
finally
finally는 try 절이 실행되기만 한다면 반드시 실행됨을 보장하는 루틴이다. try 절 안에서 예외를 던진다면 catch 문에서 예외를 처리하고 finally가 실행된다. try 절 안에서 return을 하였거나 처리되지 않은 예외를 던진다하더라도 반드시 실행되는 구문이다.
try
{
dbconn.Open(); // 데이터베이스 실행
}
catch(Exception e)
{
Console.WriteLine("예외 {0}", e.Message);
}
finally
{
dbconn.Close();
}
'C# > C# 기본' 카테고리의 다른 글
[C#] 람다식 (0) | 2021.10.15 |
---|---|
[C#] 대리자와 이벤트 (0) | 2021.10.15 |
[C#] 배열 / 컬렉션 / 인덱서 (0) | 2021.10.11 |
[C#] 프로퍼티 (0) | 2021.10.08 |
[C#] 인터페이스와 추상 클래스 (0) | 2021.10.07 |