C#프로그래밍 16 : 일반화 프로그래밍

LeeWonjin·2022년 5월 19일
0

[학부]C#프로그래밍

목록 보기
16/21

일반화 메소드

같은 동작을 수행하지만 매개변수 자료형이 다른 메소드가 있을 수 있다.
오버로딩으로 해결하기에는 버겁고 비효율적인 경우에, 일반화 메소드를 사용하면 좋다.

아래와 같이 일반화 메소드를 선언할 수 있다.
<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타입을 사용하므로 성능문제 발생
어떤 자료형을 사용할지 결정할 수 있는 경우, 일반화 컬렉션으로 컬렉션의 성능문제 해결

List<T>

비일반화 컬렉션 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<T>

비일반화 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<T>

비일반화 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');
}

Hashtable

일반화된 해시테이블이라고 할 수 있는, Dictionary가 존재한다.
MSDN https://docs.microsoft.com/ko-kr/dotnet/api/system.collections.generic.dictionary-2?view=net-6.0

foreach<T>가 가능한 객체

기존에 학습한 "foreach가 가능한 객체"의 일반화 버전이다.

  1. IEnumerable<T>인터페이스를 상속하는 형식이어야 foreach가 가능하다.
  2. IEnumerable<T>GetEnumerator()메소드가 있다.
  3. GetEnumerator()메소드는IEnumerator<T>객체를 반환한다.
  4. IEnumerator<T>인터페이스는 아래 메소드, 프로퍼티를 갖는다
  • boolean MoveNext()
  • void Reset()
  • Object Current{ get; }
  • T Current { get; } : 컬렉션의 현재 요소 반환
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
        }
    }
}
profile
노는게 제일 좋습니다.

0개의 댓글