c# 개념잡기 – Generic

박싱 & 언박싱

  • 값타입 –박싱–> 참조타입
  • 참조타입 –언박싱–> 값타입
ArrayList al = new ArrayList();
al.Add(100);                        // ArrayList는 object를 저장함. 따라서 100(값타입)은 참조타입으로 box되어 저장됨
int val = (int)al[0];               // al[0]를 int(값타입)로 사용하기 위해 unboxing됨

값타입

  • 단순타입 : int, byte, char, float, decimal, bool 등
  • 열거형타입 : enum
  • 구조체타입 : struct
  • nullable 타입 : int?, double? 등

참조타입

  • 클래스 타입 : object, string, class
  • 인터페이스 타입 : interface
  • 배열타입 : Array(int[], int[,] 등)
  • 델리게이트 타입 : delegate

Generic

  • boxing, unboxing 에 사용되는 리소스 소비 해결
  • Generic에 사용되는 타입은 일반적으로 T로 명명
List<int> list = new List<int>();  // int 타입을 제네릭 타입 파라메터로 지정
list.Add(100);                     // int 데이타만 저장가능
int val2 = list[0];                // int 타입이기에 형변환 필요없음

Generic 제한사항

  • 제네릭 적용 클래스는 ContextBoundObject 클래스로 파생될 수 없음 : 런타임 오류발생
  • 제네릭은 열거형에 사용못함
  • 제네릭은 Reflection을 이용해 생성되는 동적메소드에 사용못함.

Generic 제약사항

  • 제네릭이 클래스레벨에서 지정될때 where로 제약을 지정할 수 있음

.Net Framework에서 제공하는 Generic

  • Dictionary<TKey, TValue> : Hashtable의 제네릭 버전. Key & Value가 한쌍. Key는 유일
  • List<T> : ArrayList의 제네릭 버전. 배열의 크기를 동적으로 구성
  • SortedList<TKey, TValue> : Dictionary + List. Key & Value가 한쌍. 동적 배열. Key값으로 정렬됨.
  • LinkedList<T> : 새로생김.
  • Queue<T> : Queue의 제네릭 버전
  • Stack<T> : Stack의 제네릭 버전
타입제네릭비제네릭제네릭 네임스페이스
클래스List<T>ArrayListSystem.Collections.Generic
Dictonary<TKey, TValue>HashTable
SortedList<TKey, TValue>SortedList
Stack<T>Stack
Queue<T>Queue
LinkedList<T>
ReadOnlyCollection<T>System.Collections.ObjectModel
KeyedCollection<TKey, TValue>
인터페이스IList<T>IListSystem.Collections.Generic
IDictonary<TKey, TValue>IDictonary
ICollection<T>ICollection
IEumerator<T>IEumerator
IEumerable<T>IEumerable
IComparer<T>IComparer
IComparable<T>IComparable
using System;
using System.Collections;
using System.Collections.Generic;

namespace GenericTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 박싱 & 언박싱------------------
            // boxing & unboxing 발생함
            ArrayList al = new ArrayList();
            al.Add(1);                         // ArrayList는 object를 저장함. 따라서 100(값타입)은 참조타입으로 box되어 저장됨
            int val = (int)al[0];              // al[0]를 int(값타입)로 사용하기 위해 unboxing됨
            Console.WriteLine(val);

            // boxing & unboxing 발생 안함
            List<int> list = new List<int>();  // int 타입을 제네릭 타입 파라메터로 지정
            list.Add(2);                       // int 데이타만 저장가능
            int val2 = list[0];                // int 타입이기에 형변환 필요없음
            Console.WriteLine(val2);


            // 제네릭 선언------------------
            // class를 제네릭으로 선언
            GenericDeclare1<int> gd = new GenericDeclare1<int>();
            gd.GenericProperty = 3;
            Console.WriteLine(gd.GenericProperty);

            // method를 제네릭으로 선언
            GenericDeclare2 gd2 = new GenericDeclare2();
            Console.WriteLine(gd2.GenericMethod<int>(4));


            // class를 제네릭으로 선언 - where 제약------------------
            //GC1<int> gc1 = new GC1<int>();  --> 오류발생 : int는 IDispose를 구현한 놈이 아님
            GC1<TestGC1> gc1 = new GC1<TestGC1>();

            //GC2<int, string> gc2 = new GC2<int, string>(); --> 오류발생 : int/string은 class/struct가 아님
            GC2<TestGC2Class, TestGC2Strunct> gc2 = new GC2<TestGC2Class, TestGC2Strunct>();

            //GC3<int> gc3 = new GC3<int>(); --> 오류발생 : int는 어쨓든 아님
            //GC3<TestGC3_1> gc3 = new GC3<TestGC3_1>();  --> 오류발생 : TestGC3_1 은 IDispose를 구현했지만, 생성자에 파라메터가 있음
            GC3<TestGC3_2> gc3 = new GC3<TestGC3_2>();

            // TODO : 이거 잘 모르겠다.. 나중에 수정하자
            GC4 gc4 = new GC4();
        }
    }



    #region 테스트용 클래스들

    class TestGC1 : IDisposable
    {
        public void Dispose()
        {
        }
    }

    class TestGC2Class
    {
    }

    struct TestGC2Strunct
    {
    }

    class TestGC3_1 : IDisposable
    {
        public TestGC3_1(int i)
        {
        }

        public void Dispose()
        {
        }
    }

    class TestGC3_2 : IDisposable
    {
        public TestGC3_2()
        {
        }

        public void Dispose()
        {
        }
    }
    #endregion



    /// <summary>
    /// class를 제네릭으로 선언
    /// </summary>
    class GenericDeclare1<T1>
    {
        private T1 val;

        public T1 GenericProperty
        {
            get { return val; }
            set { this.val = value; }
        }
    }

    class GenericDeclare2
    {
        /// <summary>
        /// 메소드를 제네릭으로 선언
        /// </summary>
        public T2 GenericMethod<T2>(T2 arg)
        {
            return arg;
        }
    }




    #region Class를 제네릭으로 구현시 where로 제약하는 예제
    /// <summary>
    /// T는 IDisposable 인터페이스를 구현해야함
    /// </summary>
    class GC1<T>
        where T : IDisposable
    {
    }

    /// <summary>
    /// T는 클래스여야함
    /// U는 구조체여야함
    /// </summary>
    class GC2<T, U>
        where T : class
        where U : struct
    {
    }

    /// <summary>
    /// T는 IComparable을 구현해야하고
    /// 파라메터가 없는 - "new()" - 기본 생성자를 가진 T 이다
    /// </summary>
    class GC3<T>
        where T : IDisposable, new()
    {
    }

    /// <summary>
    /// 델리게이트에 파라미터가 없는 - "new()" - 기본생성자를 가진 T
    /// </summary>
    class GC4
    {
        delegate T GenericDelegate<T>(T val) where T : new();
        //delegate int GenericDelegate2(int val);
    }
    #endregion
}

 

댓글 남기기