같은 동작을 수행하지만 매개변수 자료형이 다른 메소드가 있을 수 있다.
오버로딩으로 해결하기에는 버겁고 비효율적인 경우에, 일반화 메소드를 사용하면 좋다.
아래와 같이 일반화 메소드를 선언할 수 있다.
<T>
의 T는 데이터 형식의 식별자인데, T로 쓰는게 국룰이다.
( )
안의 매개변수에 나타난 T a
는 T자료형의 a라는 매개변수다.
한정자 반환형 메소드식별자<T>(T a, T b, ... T z) { ... }
아래와 같이 구현할 수 있다.
class Program
{
static void Print<T>(T[] arr)
{
foreach(T item in arr)
Console.Write($" {item}");
}
static public void Main(string[] Args)
{
Print(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
// 1 2 3 4 5 6 7 8 9
}
}
아래와 같이 선언한다.
class 클래스식별자<T> {
T 필드식별자; <--- T자료형의 필드
한정자 T 메소드식별자(){ ... } <--- 자료형 T를 리턴하는 메소드
}
아래 예제에서는 형식 매개변수가 T, U 2개이다.
namespace Program
{
class Printer<T, U>
{
private T[] TList = new T[0];
private U[] UList = new U[0];
public void Add(T item)
{
Array.Resize<T>(ref TList, TList.Length + 1);
TList[TList.Length - 1] = item;
}
public void Add(U item)
{
Array.Resize<U>(ref UList, UList.Length + 1);
UList[UList.Length - 1] = item;
}
public void PrintList()
{
Console.Write($"[ T ] :");
foreach (T item in TList)
Console.Write($" {item}");
Console.Write($"\n[ U ] :");
foreach (U item in UList)
Console.Write($" {item}");
Console.WriteLine();
}
}
class Program
{
static public void Main(string[] Args)
{
Printer<int, bool> p = new Printer<int, bool>();
p.Add(5);
p.Add(3);
p.Add(53);
p.Add(true);
p.Add(false);
p.Add(11);
p.PrintList();
// [T] : 5 3 53 11
// [U] : True False
}
}
}
일반화 메소드 또는 일반화 클래스에서 T의 형식을 제약할 수 있다.
그 목록은 아래와 같다.
where T : struct
: 값형식
where T : class
: 참조형식
where T : new()
: 매개변수 없는 생성자가 존재하는 클래스
where T : CA
: CA
클래스의 자식클래스
where T : I
: I
인터페이스를 상속하는 클래스
where T : IA, IB, IC
: IA
, IB
, IC
3개 인터페이스를 상속하는 클래스
class Something<T> where T : struct
{
public void Print(T[] arr)
{
foreach (T item in arr)
Console.Write($" {item}");
Console.WriteLine();
}
}
class Program
{
static void Main(string[] args)
{
Something<int> something = new Something<int>();
something.Print(new int[] { 0, 2, 3, 4, 5, 6, 7 });
// 컴파일에러
// something.Print(new string[] { "aa", "bbb", "abc" });
}
}
기존에 학습한 컬렉션은 object타입을 사용하므로 성능문제 발생
어떤 자료형을 사용할지 결정할 수 있는 경우, 일반화 컬렉션
으로 컬렉션
의 성능문제 해결
비일반화 컬렉션 ArrayList
와 동일
static void Main(string[] args)
{
List<int> a = new List<int>();
a.Add(5);
a.Add(3);
a.Add(53);
foreach (int i in a)
Console.Write($" {i}"); // 5 3 53
Console.WriteLine();
a.RemoveAt(0);
a.RemoveAt(0);
foreach (int i in a)
Console.Write($" {i}"); // 53
}
비일반화 Queue와 동일
static void Main(string[] args)
{
Queue<string> q = new Queue<string>();
q.Enqueue("abc");
q.Enqueue("zzz");
while(q.Count > 0)
Console.Write($" {q.Dequeue()}"); // abc zzz
// q.Enqueue(53);
// 컴파일에러 CS1503 1 인수: 'int'에서 'string'(으)로 변환할 수 없습니다.
}
비일반화 Stack과 동일
static void Main(string[] args)
{
Stack<short> st = new Stack<short>();
st.Push(1);
st.Push(2);
st.Push(123);
while (st.Count > 0)
Console.Write($" {st.Pop()}"); // 123 2 1
// 컴파일에러 CS1503 1 인수: 'char'에서 'short'(으)로 변환할 수 없습니다.
// st.Push('a');
}
일반화된 해시테이블이라고 할 수 있는, Dictionary가 존재한다.
MSDN https://docs.microsoft.com/ko-kr/dotnet/api/system.collections.generic.dictionary-2?view=net-6.0
기존에 학습한 "foreach가 가능한 객체"의 일반화 버전이다.
IEnumerable<T>
인터페이스를 상속하는 형식이어야 foreach가 가능하다.IEnumerable<T>
는 GetEnumerator()
메소드가 있다.GetEnumerator()
메소드는IEnumerator<T>
객체를 반환한다.IEnumerator<T>
인터페이스는 아래 메소드, 프로퍼티를 갖는다namespace Program
{
class foreachGanoong<T> : IEnumerator<T>, IEnumerable<T>
{
private T[] arr;
int position = -1;
public foreachGanoong() { arr = new T[0]; }
public T this[int idx]
{
get { return arr[idx]; }
set {
if(idx >= arr.Length)
Array.Resize<T>(ref arr, idx + 1);
arr[idx] = value;
}
}
object IEnumerator.Current { get { return arr[position]; } }
public T Current { get { return arr[position]; } }
public bool MoveNext()
{
if(position == arr.Length - 1)
{
this.Reset();
return false;
}
position += 1;
return true;
}
public void Reset() { position = -1; }
IEnumerator IEnumerable.GetEnumerator() { return this; }
public IEnumerator<T> GetEnumerator() { return this; }
public void Dispose() { Console.Write(" --- END : foreach"); }
}
class Program
{
static void Main(string[] args)
{
foreachGanoong<int> a = new foreachGanoong<int>();
a[0] = 5;
a[2] = 3;
a[3] = 53;
a[5] = 5353;
foreach (int item in a)
Console.Write($" {item}");
// 5 0 3 53 0 5353 --- END : foreach
Console.WriteLine();
foreach (int item in a)
Console.Write($" {item}");
// 5 0 3 53 0 5353 --- END : foreach
}
}
}