반응형
1. 일반화 프로그래밍
일반화: 특수한 개념으로부터 공통된 개념을 찾아 묶는 것
일반화 프로그래밍: 데이터 형식을 일반화하는 것
2. 일반화 메소드
한정자 반환 형식 메소드 이름 <형식 매개 변수> (매개 변수 목록)
{
// ...
}
제네릭 기본
static void Main(string[] args)
{
int[] arr = { 1, 2, 3 };
Console.WriteLine(First(arr));
}
static T First<T>(T[] arr)
{
return arr[0];
}
class Program
{
// 정적 메소드로 만들지 않으면 객체.함수명으로 호출해야 함
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);
}
}
3. 일반화 클래스
제네릭 클래스
class Program
{
static void Main(string[] args)
{
var range = new Range<int>(1, 2);
var range2 = new Range<char>('a', 'z');
}
}
class Range<T>
{
T Start;
T End;
public Range(T start, T end)
{
Start = start;
End = end;
}
}
class Program
{
static void Main(string[] args)
{
Array_Generic<int> intArr = new Array_Generic<int>();
// Array_Int intArr = new Array_Int(); // 와 동일
Array_Generic<double> doubleArr = new Array_Generic<double>();
// Array_Double doubleArr = new Array_Double(); // 와 동일
}
}
#region [1. 일반화 클래스 X - 똑같은 기능을 하는 두 클래스]
class Array_Int
{
private int[] array;
// ...
public int GetElement(int index) { return array[index]; }
}
class Array_Double
{
private double[] array;
// ...
public double GetElement(int index) { return array[index]; }
}
#endregion
#region [2. 일반화 클래스]
class Array_Generic<T>
{
private T[] array;
// ...
public T GetElement(int index) { return array[index]; }
}
#endregion
활용 예제
class MyList<T> // 가변길이 배열
{
private T[] array;
// 생성자: 길이가 3인 새로운 배열 생성
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); // Array를 리사이즈
Console.WriteLine($"Array Resized: {array.Length}");
}
array[index] = value;
}
}
public int Length { get { return array.Length; } }
}
class Program
{
static void Main(string[] args)
{
MyList<string> str_list = new MyList<string>();
str_list[0] = "adb";
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]);
}
Console.WriteLine();
}
}
4. 형식 매개 변수 제약하기
제약 | 설명 |
where T : struct | T는 값 형식 |
where T : class | T는 참조 형식 |
where T : new() | T는 매개변수가 없는 생성자 존재 |
where T : 클래스명 | T는 해당 클래스의 파생 클래스 |
where T : 인터페이스명 | T는 해당 인터페이스 구현 |
where T : U | T는 다른 형식 매개 변수 U의 파생 |
static void Main(string[] args)
{
int[] a = { 1, 2, 3 };
string[] b = { "1", "2", "3" };
//First(a); //Error
First(b);
}
static T First<T>(T[] arr) where T: class
{
return arr[0];
}
interface iClass
{
// ...
}
class MyClass
{
// ...
}
class MyList<T> where T : MyClass // Type 제한: MyClass로부터 상속받는 형식이어야 할 것
{
// ...
}
class MyList2<T> where T : iClass // Type 제한: iClass를 반드시 구현해야 할 것
{
// ...
}
class MyClass<T> where T : class // Type 제한: 참조 형식이어야 할 것
{
// ...
}
class Program
{
static void CopyArray<T>(T[] source, T[] target) where T : struct // Type 제한: 값 형식이어야 할 것
{
for (int i = 0; i < source.Length; i++) {
target[i] = source[i];
}
}
static void CopyArray<T>() where T : new() // Type 제한: 반드시 매개 변수가 없는 생성자가 있어야 할 것
{
}
static void Main(string[] args)
{
}
}
활용 예제
class StructArray<T> where T : struct // T는 값 형식일 것
{
public T[] Array { get; set; }
public StructArray(int size) // 생성자: size 크기의 배열을 생성
{
Array = new T[size];
}
}
class RefArray<T> where T : class // T는 참조 형식일 것
{
public T[] Array { get; set; }
public RefArray(int size) // 생성자: size 크기의 배열을 생성
{
Array = new T[size];
}
}
class Base { } // Base 클래스
class Derived : Base { } // Base 클래스를 상속받은 Derived 클래스
class BaseArray<U> where U : Base // U는 Base 클래스의 파생 클래스일 것
{
public U[] Array { get; set; }
public BaseArray(int size) // 생성자: size 크기의 배열을 생성
{
Array = new U[size];
}
public void CopyArray<T>(T[] Source) where T : U // T는 U를 상속받은 형식일 것
{
// Source의 모든 요소를 인덱스 0부터 시작하여 Array에 복사
Source.CopyTo(Array, 0);
}
}
class Program
{
public static T CreateInstance<T>() where T : new()
{
return new T();
}
static void Main(string[] args)
{
StructArray<int> a = new StructArray<int>(3);
a.Array[0] = 0;
a.Array[1] = 1;
a.Array[2] = 2;
RefArray<StructArray<double>> b = new RefArray<StructArray<double>>(3);
b.Array[0] = new StructArray<double>(5);
b.Array[1] = new StructArray<double>(10);
b.Array[2] = new StructArray<double>(1005);
BaseArray<Base> c = new BaseArray<Base>(3);
c.Array[0] = new Base();
c.Array[1] = new Derived();
c.Array[2] = CreateInstance<Base>();
BaseArray<Derived> d = new BaseArray<Derived>(3);
d.Array[0] = new Derived(); // Base 형식은 여기에 할당할 수 없다.
d.Array[1] = CreateInstance<Derived>();
d.Array[2] = CreateInstance<Derived>();
BaseArray<Derived> e = new BaseArray<Derived>(3);
e.CopyArray<Derived>(d.Array);
}
}
5. 일반화 컬렉션
List<T>
class Program
{
static void Main(string[] args)
{
// List<T>: ArrayList와 달리 형식 매개 변수 T 외에는 입력을 허용하지 않음
List<int> list = new List<int>();
for (int i = 0; i < 5; i++)
list.Add(i);
foreach (int element in list)
Console.WriteLine($"{element}");
Console.WriteLine();
list.RemoveAt(2);
foreach (int element in list)
Console.WriteLine($"{element}");
Console.WriteLine();
list.Insert(2, 2);
foreach (int element in list)
Console.WriteLine($"{element}");
Console.WriteLine();
}
}
Queue<T>
class Program
{
static void Main(string[] args)
{
Queue<int> queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
queue.Enqueue(4);
queue.Enqueue(5);
while (queue.Count > 0) {
Console.WriteLine(queue.Dequeue());
}
}
}
Stack<T>
class Program
{
static void Main(string[] args)
{
Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
stack.Push(4);
stack.Push(5);
while (stack.Count > 0) {
Console.WriteLine(stack.Pop());
}
}
}
class Program
{
static void Main(string[] args)
{
Dictionary<string, string> dic = new Dictionary<string, string>();
dic["하나"] = "one";
dic["둘"] = "two";
dic["셋"] = "three";
dic["넷"] = "four";
dic["다섯"] = "five";
foreach (var item in dic.Keys) {
Console.WriteLine(dic[item]);
}
}
}
6. foreach를 사용할 수 있는 일반화 클래스
IEnumerable과 IEnumerator
[C#] IEnumerable, IEnumerator 그리고 yield
enumerate 영어로 수를 세다. 카운팅 하다! 두 인터페이스는 열거자와 관련이 있다.(반복자와 동일한…것 같다. 아닐수도..) using System.Collections; C#의 모든 Collections 컬렉션은 IEnumerable, IEnumerator를 상
ansohxxn.github.io
foreach를 사용할 수 있는 클래스를 만들기 위해서는,
IEnumerable 인터페이스와 IEnumerator 인터페이스를 상속하고 메소드와 프로퍼티를 구현해야 함
메소드 | 설명 |
IEnumerator GetEnumerator | IEnumerator 형식의 객체를 반환(IEnumerable로부터 상속받은 메소드) |
IEnumerator<T> GetEnmerator | IEnumerator<T> 형식의 객체를 반환 |
메소드 | 설명 |
boolean MoveNext() | 다음 요소로 이동합니다. 컬렉션의 끝을 지난 경우에는 false, 이동이 성공한 경우에는 true를 반환합니다. |
void Reset() | 컬렉션의 첫 번째 위치의 "앞"으로 이동합니다. 첫 번째 위치가 0번일 때, Reset()을호출하면 -1번으로 이동합니다. 첫 번째 위치로의 이동은 MoveNext()를 호출한 다음에 이루어집니다. |
Object Current { get; } | 컬렉션의 현재 요소를 반환(IEnumerator로부터 상속받은 프로퍼티). |
T Current { get; } | 컬렉션의 현재 요소를 반환합니다. |
// IEnumerable: 열거자를 리턴하는 Getter의 Getter
// IEnumerator: 데이터를 리턴(Getter)하는 열거자
class MyList<T> : IEnumerable<T>, IEnumerator<T>
{
#region [MyList getter, setter, Length]
private T[] array;
int position = -1;
// 생성자: array를 길이가 3인 배열로 초기화
public MyList() { array = new T[3]; }
public T this[int index]
{
get { return array[index]; }
set { // array 길이보다 index가 클 경우 array를 리사이즈
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; }
}
#endregion
#region [IEnumerator 구현]
// IEnumerator를 리턴하는 모든 함수는 ref, out 매개변수가 허용되지 않으며, 또한 람다 함수에서 사용할 수도 없음
// IEnumerable: 열거자 IEnumerator를 Get하는 데 필요한 인터페이스
// IEnumerator: 열거자를 구현하는 데 필요한 인터페이스
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < array.Length; i++) {
yield return (array[i]);
}
}
// GetEnumerator: 컬렉션을 반복하는 데 사용할 수 있는 IEnumerator를 리턴
IEnumerator IEnumerable.GetEnumerator()
{
for (int i = 0; i < array.Length; i++) {
yield return (array[i]);
// array[i]가 IEnumerator로 형변환되어 리턴됨
}
}
// Current: 현재 위치의 데이터를 object 타입으로 리턴
public T Current
{
get { return array[position]; }
// T 객체인 array[position]을 리턴함
// 이건 IEnumerable 인터페이스를 구현한 프로퍼티가 아님
}
object IEnumerator.Current
{
get { return array[position]; }
// array[position]을 System.Object로 업캐스팅 형변환하여 리턴함
// 이건 IEnumerable 인터페이스를 구현한 프로퍼티가 맞음
}
// MoveNext: 다음 위치로 이동하는데, 다음 위치에 데이터 있으면 true, 없으면 false 리턴
public bool MoveNext()
{
if (position == array.Length - 1)
{
Reset();
return false;
}
position++;
return (position < array.Length);
}
// Reset: 인덱스를 초기 상태 위치로 이동시킴
public void Reset()
{
position = -1;
}
public void Dispose() { }
#endregion
}
class Program
{
static void Main(string[] args)
{
MyList<string> str_list = new MyList<string>();
str_list[0] = "adb";
str_list[1] = "def";
str_list[2] = "ghi";
str_list[3] = "jkl";
str_list[4] = "mno";
foreach (string str in str_list)
Console.WriteLine(str);
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;
foreach (int no in int_list)
Console.WriteLine(no);
}
}
활용예제
class Program
{
static void Main(string[] args)
{
#region [제네릭]
Print(10);
Print("10");
Print(true);
#endregion
#region [제네릭 X]
Print<int>(10);
Print<string>("10");
Print<bool>(true);
#endregion
}
#region [제네릭]
public static void Print<T>(T data)
{
Console.WriteLine(data);
}
#endregion
#region [제네릭 X]
public static void Print(int data)
{
Console.WriteLine(data);
}
public static void Print(string data)
{
Console.WriteLine(data);
}
public static void Print(bool data)
{
Console.WriteLine(data);
}
#endregion
#region [제네릭]
// CodeBase를 상속받은 애만 처리!
public static void Save<T>(T data) where T : CodeBase
{
// todo...
Console.WriteLine(data.Code);
Console.WriteLine(data.Name);
}
public static void SaveChit<T>(T data) where T : ChitBase
{
// todo...
Console.WriteLine(data.Prod);
Console.WriteLine(data.ProdCode);
}
#endregion
#region [제네릭 X]
public static void Save(Product data)
{
// todo...
Console.WriteLine(data.Code);
Console.WriteLine(data.Name);
}
public static void Save(Cust data)
{
// todo...
Console.WriteLine(data.Code);
Console.WriteLine(data.Name);
}
#endregion
}
#region [Product, Cust]
public interface CodeBase
{
string Code { get; set; }
string Name { get; set; }
}
public class Product : CodeBase
{
public string Code { get; set; }
public string Name { get; set; }
}
public class Cust : CodeBase
{
public string Code { get; set; }
public string Name { get; set; }
}
#endregion
#region [Purchase, Sale]
public interface ChitBase
{
Product Prod { get; set; }
string ProdCode { get; set; }
}
public class Purchase : ChitBase
{
public Product Prod { get; set; }
public string ProdCode { get; set; }
}
public class Sale : ChitBase
{
public Product Prod { get; set; }
public string ProdCode { get; set; }
}
#endregion
반응형
'C# > 이것이 C#이다' 카테고리의 다른 글
14장. 람다식 (0) | 2023.05.18 |
---|---|
13장. 대리자와 이벤트 (0) | 2023.05.18 |
10장. foreach가 가능한 객체 만들기 (0) | 2023.05.18 |
9장. 프로퍼티 (0) | 2023.05.16 |
8장. 인터페이스와 추상 클래스 (0) | 2023.04.28 |