C#

[C#] Generic T 일반화 프로그래밍

Prooni 2024. 10. 8. 13:06

안녕하세요~

오늘은 C#에서 "Generic", 즉 일반화 프로그래밍에 대해 공부해요!

 

일반화특수한 개념으로부터 공통된 개념을 찾아 묶는 것이에요

그리고 우리가 일반화할 대상은 "데이터 타입" 이랍니다.

 

메소드 오버로드를 통해 여러가지 데이터 타입에 대해

메소드를 구현할 수 있지만

같은 구현 내용에 대해 데이터 타입만 40가지라면

오버로드로 40개의 함수를 구현해야겠죠?

매우 비효율적이에요

 

이때 사용하는 것이 일반화 메소드 랍니다!

일반화 메소드는 데이터 형식을 일반화한 메소드에요

선언 및 구현 형태는 아래와 같습니다.

한정자 반환형식 메소드이름<형식_매개변수> (매개변수_목록)
{
    //
}

 

매개변수 형식에 관계없이 일반화 메소드를 호출할 수 있어요!

예시코드 추가할게요

class CopyingArray
{
    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);
        }
    }
}

 

일반화 메소드가 다양한 데이터 형식의 매개변수를 사용할 수 있는것처럼

다양한 데이터 형식을 사용할 수 있는 클래스도 있어요!

바로 일반화 클래스 인데요.

 

필드, 프로퍼티, 메소드가 같은 클래스인데

데이터 형식이 다르다면

사용할 데이터 형식의 개수만큼 클래스를 생성해야겠죠

하지만 일반화 클래스를 사용한다면 하나의 클래스로 여러 데이터 형식을 적용할 수 있답니다!

 

아래 코드에서 T 자리에 다양한 데이터 형식이 올 수 있어요 

class Array_Genenric<T>
{
	private T[] array;
    // ...
    public T GetElement(int index)
    {
    	return array[index];
    }
}

 

 

아래 코드에서 Array_Generic 클래스의 형식 매개변수 T는

객체를 생성할 때 입력받은 형식으로 치환되어 컴파일되어요.

Array_Generic<int> intArr = new Array_Generic<int>();
Array_Generic<double> dblArr = new Array_Generic<double>();

 

 

형식 매개변수 T는 다양한 형식을 대신할 수 있지만

때때로 형식 매개변수에 대해 제약조건이 필요할때가 있어요!

 

이럴 때 사용하는 형식 매개변수를 사용하는 형태는 아래와 같답니다!

class MyList<T> where T : MyClass
{
    //...
}

 

where 뒤에 형식 매개 변수의 조건을 표시하는 방식이에요~

그렇다면 where절에 올 수 있는 제약조건의 종류를 한번 알아볼게요.

 

where T : struct T는 값 형식
where T : class T는 참조 형식
where T : new() T는 반드시 매개변수가 없는 생성자를 가짐
where T : 기반_클래스_이름 T는 명시한 기반 클래스의 파생 클래스여야 함
where T : 인터페이스_이름 T는 명시한 인터페이스를 반드시 구현해야함, 인터페이스 여러개 가능
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 Base { }
class Derived : Base { }
class BaseArray<U> where U : Base
{
    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);
    }
}

class MainApp
{
    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] = 1;
        a.Array[1] = 2;
        a.Array[2] = 3;

        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();
        d.Array[1] = CreateInstance<Derived>();
        d.Array[2] = CreateInstance<Derived>();

        BaseArray<Derived> e = new BaseArray<Derived>(3);
        e.CopyArray<Derived>(d.Array);
    }
}

 

 

자 그리고 컬렉션 기억나시나요?

컬렉션에도 일반화 컬렉션이 있답니다!!

컬렉션에 대해 가물가물하시다면 아래 게시물을 참고하시면 도움이 될거에요!

https://unityking.tistory.com/19

 

[C#] 컬렉션

안녕하세요!오늘은 C#의 컬렉션에 대해 공부해요~ 컬렉션은 한마디로 같은 성격의 데이터 모음을 담는 자료구조를 뜻하는데요..NET에서 제공하는 여러 컬렉션들이 있지만 몇가지만 알아볼게요!

unityking.tistory.com

 

컬렉션의 경우에는 object 형태의 요소들을 담고 있어서

사용하기에는 편리하지만

데이터 검색이나 추가시에 형변환이 자주 일어나

오버헤드의 한계점이 있답니다!

 

일반화 컬렉션을 통해 이러한 컬렉션의 한계점을 극복할 수 있어요!!

대표적인 컬렉션인

List<T>,

Queue<T>,

Stack<T>,

Dictionary<TKey, TValue>

를 통해서 각 컬렉션의 형식을 지정할 수 있어요.

그럼 형변환으로 인한 오버헤드의 문제점이 해결될 수 있어요!

 

 

마지막으로!!

foreach를 사용할 수 있는 일반화 클래스에 대해 알아볼게요!

 

foreach문을 사용하기 위해서는

대상 클래스가 IEnumerable을 상속받아야 해요!

그런데 컬렉션과 마찬가지로

이 경우에도 foreach문을 순회할때

형식 변환을 수행하므로 오버로드가 걸릴 수 있다는 문제가 있어요!

 

이때 해결할 수 있는 방법이 바로

IEnumerable을 상속받은 IEnumerable<T>를 사용하는거에요!

 사용방법은 IEnumerable을 사용하는 것과 같지만

구현 메소드가 하나 추가되어요!

 

IEnumerable GetEnumerator() IEnumerator 형식의 객체를 반환(IEnumerable로부터 상속받은 메소드)
IEnumerator<T> GetEnumerator() IEnumerator<T> 형식의 객체를 반환

 

이렇게 두가지 메소드를 구현하면 된답니다.

 

예시코드 추가할게요.

class MyList<T> : IEnumerable<T>, IEnumerator<T>
{
    private T[] array;
    int position = -1;

    public MyList()
    {
        array = new T[3];
    }

    public T this[int index]
    {
        get
        {
            return array[index];
        }

        set
        {
            if (index >= array.Length)
            {
                Array.Resize(ref array, index + 1);
                Console.WriteLine($"Array Resized : {array.Length}");
            }

            array[index] = value;
        }
    }

    public int Length
    {
        get { return array.Length; }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return this;
    }

    public T Current
    {
        get { return array[position]; }
    }

    object IEnumerator.Current
    { 
        get { return array[position]; }
    }

    public bool MoveNext()
    {
        if(position == array.Length - 1)
        {
            return false;
        }

        position++;
        return (position < array.Length);
    }

    public void Reset()
    {
        position = -1;
    }

    public void Dispose()
    {

    }
}

class MainApp
{
    static void Main(string[] args)
    {
        MyList<string> str_list = new MyList<string>();
        str_list[0] = "abc";
        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] = 1;
        int_list[1] = 2;
        int_list[2] = 3;
        int_list[3] = 4;
        int_list[4] = 5;             

        foreach(int no in int_list)
        {
            Console.WriteLine(no);
        }
    }
}

'C#' 카테고리의 다른 글

[C#] 대리자와 이벤트 (delegate 와 event)  (5) 2024.10.10
[C#] 예외 처리  (1) 2024.10.09
[C#] 인덱서  (0) 2024.10.07
[C#] 컬렉션  (0) 2024.10.07
[C#] 배열  (0) 2024.10.07