본문으로 바로가기
반응형

 일반화 프로그램(제네릭 프로그램)은 데이터 형식을 일반화하는 기법이다. 예를들어 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