일반화 프로그램(제네릭 프로그램)은 데이터 형식을 일반화하는 기법이다. 예를들어 int 배열의 요소들을 출력하는 PrintArray(int[] a) 메서드를 만들었다고 가정해보자. 그런데 double 배열도 출력할 일이 생겼다. 이럴땐 어떻게 해야할까? 메서드 중복을 사용해서 PrintArray(double[] a) 메서드를 만들어서 사용하면 된다.
void PrintArray(int[] a) { ... }
void PrintArray(double[] a) { ... }
하지만 더 좋은 방법이 있다. 바로 일반화 프로그램인데, 일반화 프로그램으로 만든 메서드를 일반화 메서드라고한다. 위에서 메서드 중복으로 만든 두 개의 PrintArray() 메서드는 다음과같이 일반화 메서드로 만들면 하나로 처리가 가능하다.
void PrintArray<T>(T[] a) { ... }
여기서 사용된 T는 type이라는 뜻을 갖는 기호이다. 보통 T를 쓰지만 T가 아니고 다른 문자를 써도 상관없다. <T>와 같이 삿갓 괄호 안에 T를 쓰면 T는 형식 매개변수가되어 int, double, 등의 형식으로 대체된다.
예제를 살펴보자.
제네릭 프로그래밍(메서드에 적용)
우선 메서드가 중복되는 예제를 작성했다.
using System;
class Program
{
static void ArrayIntPrint(int[] Num)
{
for (int Temp = 0; Temp < Num.Length; ++Temp)
{
Console.Write("{0} ", Num[Temp]);
}
Console.WriteLine();
//foreach (int Temp in Num)
//{
// Console.WriteLine("{0}", Temp);
//}
}
static void ArraydoublePrint(double[] Num)
{
for (int Temp = 0; Temp < Num.Length; ++Temp)
{
Console.Write("{0} ", Num[Temp]);
}
Console.WriteLine();
}
static void ArrayStringPrint(String[] Num)
{
for (int Temp = 0; Temp < Num.Length; ++Temp)
{
Console.Write("{0} ", Num[Temp]);
}
Console.WriteLine();
}
static void Main(string[] args)
{
int[] Numbers1 = {1,3,5,7,9 };
ArrayIntPrint(Numbers1);
Console.WriteLine();
double[] Numbers2 = { 1.1, 3.1, 5.1, 7.1, 9.1 }; // float형은 f를 붙여야하고, 그냥 실수면 double형이다.
ArraydoublePrint(Numbers2);
Console.WriteLine();
string[] Numbers3 = { "일","이","삼","사"}; // 문자열은 ""를 붙여줘야한다.
ArrayStringPrint(Numbers3);
Console.WriteLine();
}
}
코드의 중복을 제거해보자. 메서드를 1개로 줄이고, <>를 주목해보자. T자리는 아무글자를 써도되는데 Type의 보통 T를 많이쓴다.
using System;
class Program
{
static void ArrayPrint<T>(T[] Num)
{
for (int Temp = 0; Temp < Num.Length; ++Temp)
{
Console.Write("{0} ", Num[Temp]);
}
Console.WriteLine();
}
static void Main(string[] args)
{
int[] Numbers1 = { 1, 3, 5, 7, 9 };
double[] Numbers2 = { 1.1, 3.1, 5.1, 7.1, 9.1 }; // float형은 f를 붙여야하고, 그냥 실수면 double형이다.
string[] Numbers3 = { "일", "이", "삼", "사" }; // 문자열은 ""를 붙여줘야한다.
ArrayPrint<int>(Numbers1);
ArrayPrint<double>(Numbers2);
ArrayPrint<string>(Numbers3);
}
}
C++에서는 템플릿이라고 부름.
JAVA, C#에서는 제너릭이라고 부른다.
성능차이는 안나지만, 만드는 시간이 다르고, 단순반복은 컴퓨터한테 시키고 문법만 적용시켜서 타입에따라서 코드를 자동으로 만들어준다.
T는 메서드와 괄호 사이에 꼭! 있어야한다.(이것은 C#문법이므로 외워야하는것임)
인자가 2개인 제네릭 프로그래밍을 사용해보자.
int 배열을 2개씩 선언해줘서 int1에 있는 값을 int2에 대입하는 예제이다.
같은 방법으로 double형도 똑같이 작성했다.
using System;
class Program
{
static void ArrayPrint<T>(T[] Num)
{
for (int Temp = 0; Temp < Num.Length; ++Temp)
{
Console.Write("{0} ", Num[Temp]);
}
Console.WriteLine();
}
static void ArrayIntCopy(int[] Dst, int[] Src)
{
for(int Temp = 0; Temp < Dst.Length; ++Temp)
{
Dst[Temp] = Src[Temp];
}
}
static void ArraydoubleCopy(double[] Dst, double[] Src)
{
for (int Temp = 0; Temp < Dst.Length; ++Temp)
{
Dst[Temp] = Src[Temp];
}
}
static void Main(string[] args)
{
int[] ArrayInt1 = {2, 4, 6, 8}; // 힙영역에 만들어서 값을 넣은거임
int[] ArrayInt2 = new int[4]; // 힙영역에 만들기만했고, 0으로 채워져있는거임.
ArrayIntCopy(ArrayInt2, ArrayInt1);
ArrayPrint<int>(ArrayInt2);
double[] Arraydouble1 = { 2.1, 4.1, 6.1, 8.1 };
double[] Arraydouble2 = new double[4];
ArraydoubleCopy(Arraydouble2, Arraydouble1);
ArrayPrint<double>(Arraydouble2);
}
}
주석 부분은 하드코딩을 방지해서 반복문을 통해 줄였다.
using System;
class Program
{
static void ArrayPrint<T>(T[] Num)
{
for (int Temp = 0; Temp < Num.Length; ++Temp)
{
Console.Write("{0} ", Num[Temp]);
}
Console.WriteLine();
}
static void ArrayCopy<T>(T[] Dst, T[] Src)
{
for (int Temp = 0; Temp < Dst.Length; ++Temp)
{
Dst[Temp] = Src[Temp];
}
}
//static void ArrayIntCopy(int[] Dst, int[] Src)
//{
// for(int Temp = 0; Temp < Dst.Length; ++Temp)
// {
// Dst[Temp] = Src[Temp];
// }
//}
//static void ArraydoubleCopy(double[] Dst, double[] Src)
//{
// for (int Temp = 0; Temp < Dst.Length; ++Temp)
// {
// Dst[Temp] = Src[Temp];
// }
//}
static void Main(string[] args)
{
int[] ArrayInt1 = {2, 4, 6, 8}; // 힙영역에 만들어서 값을 넣은거임
int[] ArrayInt2 = new int[4]; // 힙영역에 만들기만했고, 0으로 채워져있는거임.
ArrayCopy<int>(ArrayInt2, ArrayInt1);
ArrayPrint<int>(ArrayInt2);
double[] Arraydouble1 = { 2.1, 4.1, 6.1, 8.1 };
double[] Arraydouble2 = new double[4];
ArrayCopy<double>(Arraydouble2, Arraydouble1);
ArrayPrint<double>(Arraydouble2);
}
}
제네릭은 두개를 한꺼번에 선언해줄수도 있다.
using System;
class Program
{
static void TwoPrint<T1, T2>(T1 Arg1, T2 Arg2)
{
Console.WriteLine(Arg1);
Console.WriteLine(Arg2);
}
static void Main(string[] args)
{
TwoPrint(3, 2.1);
TwoPrint("헬로", 4.0f);
}
}
메인메서드부분에 <>를 안써도 오류가 뜨지않았는데 왜인지는 모르겠다...
제네릭 프로그래밍(클래스)
제네릭 클래스는 멤버의 형을 <T>로 표시한 클래스이다. 객체 생성 시에 그 멤버의 형이 결정된다. 일반화 클래스 하나를 예시로 들어보자.
class MyClass<T>
{
public T x;
public T Dosomething(T p) { ... }
}
클래스 이름 뒤의 <T>는 형식 매개변수라고한다. 객체가 생성될 때 < > 안에 쓰는 형이 T를 대체하게 된다. 다음의 코드에서 x는 <int>를 사용했으므로 클래스의 정의에서 T로 쓴 부분이 int로 바귀게 되어 다음과 같은 클래스가 정의된 것과 같이 동작한다. <string>을 쓴 두번째 객체 s는 T가 string으로 바뀐 클래스를 사용하게 된다.
MyClass<int> x = new Myclass<int>();
Myclass<string> s = new MyClass<string>();
class Myclass
{
public int x;
public int Dosomething(int p) { ... }
}
일반화 클래스를 사용하면 같은 구조와 동작을 갖지만 형만 다른 클래스를 중복해서 작성하지 않아도 된다.
일반 클래스를 사용한 예제
using System;
public class FACT
{
public int Value;
public void Print()
{
Console.WriteLine("FACT Value = {0}", Value);
}
}
class Program
{
static void Main(string[] args)
{
FACT obj = new FACT();
obj.Value = 100;
obj.Print();
}
}
제네릭 클래스를 사용한 예제
using System;
public class FACT<T>
{
public int Value;
public void Print()
{
Console.WriteLine("FACT Value = {0}", Value);
}
}
class Program
{
static void Main(string[] args)
{
FACT<int> obj = new FACT<int>();
obj.Value = 100;
obj.Print();
}
}
클래스의 인자를 2개로 정의해줄수도있다.
using System;
class FACT<T1, T2>
{
public T1 Value1;
public T2 Value2;
public void Print()
{
Console.WriteLine("FACT Value = {0}, {1}", Value1, Value2);
}
}
class Program
{
static void Main(string[] args)
{
FACT<int, string> obj = new FACT<int, string>();
obj.Value1 = 100;
obj.Value2 = "서맛트 팩토리";
obj.Print();
}
}
'개발자과정준비 > C#' 카테고리의 다른 글
[C#] 컬렉션, ArrayList (0) | 2020.08.24 |
---|---|
[C#] Random 클래스 (0) | 2020.08.23 |
C# 복습 (4) (0) | 2020.07.20 |
C# 복습 (3) (0) | 2020.07.14 |
C# 복습 (2) (0) | 2020.07.14 |