주녘공부일지

[C#] 제네릭 (Generic) 본문

C#/Definition, Etc

[C#] 제네릭 (Generic)

주녘 2023. 10. 27. 17:39
728x90

제네릭 (Generic)

- 서로 다른 형식들에 대해 재사용할 수 있는 코드를 작성하기 위해 사용하는 메커니즘 

형식 안정성을 높이고 캐스팅과 박싱을 줄이기 위한 수단이 됨 (일반화, 특수화) 

 

+ 제네릭 컬렉션의 자료구조를 부분적으로 직접 구현해 예제로 삼음

https://godgjwnsgur7.tistory.com/46

 

[C#] 자료구조 ( 제네릭 컬렉션 )

자료구조(Data Structure)란? 데이터를 효율적으로 접근하고 조작할 수 있게 데이터 구조를 만들어 관리하는 것 - Collections은 C#에서 지원하는 자료구조 클래스 using System.Collections.Generic; 제네릭 컬렉

godgjwnsgur7.tistory.com

1) 제네릭 형식

형식 매개변수를 선언해 사용하는 형식

- 형식 매개변수는 제네릭 형식이 실제로 사용될 때 제공되는 실제 형식을 대신할 자리를 표시하는 '자리표' 역할

 ex) 제네릭 형식 Stack<T> / 형식 매개변수 T (이름은 관례적으로 T를 사용하거나 T로 시작함)

        // 열린 형식 Stack<T>
        public class Stack<T>
        {
            int position;
            T[] data = new T[100];
            public void Push(T obj) => data[position++] = obj;
            public T Pop() => data[--position];
        }
        public static void Main()
        {
            var stack = new Stack<int>(); // 닫힌 형식 intStack 선언
            stack.Push(1);
            stack.Push(2);
            int num = stack.Pop(); // num = 2
        }

if) 만약, 제네릭 형식없이 object를 원소 형식으로 사용해 스택을 일반화 한다면?

- 모든 원소 형식에 대해 작동하도록 일반화는 가능하지만, 특정 원소 형식에 맞게 특수화되지 않음

2) 제네릭 메서드

형식 매개변수를 사용하여 선언된 메서드

 ex) 같은 형식의 변수 두 개의 값을 맞바꾸는 Swap 메서드

        public static void Swap<T>(ref T a, ref T b)
        {
            T temp = a;
            a = b;
            b = temp;
        }

        public static void Main()
        {
            int a = 1, b = 2;
            Swap(ref a, ref b);
            Swap<int>(ref a, ref b); // 명시적 지정 가능
        }

3) 형식 매개변수의 선언

- 클래스나 구조체, 인터페이스, 대리자, 메서드 선언에서만 도입 가능 ( 이외에는 사용만 가능 )

- default 키워드를 이용해 제네릭 형식 매개변수의 인스턴스의 기본 값을 지정 가능

 -> ( 참조 : null / 값 : 모든 비트가 0인 경우의 값 으로 지정됨 )

- 여러 개의 형식 매개변수 도입 가능

- 형식 매개변수의 개수가 다를 경우 제네릭 형식 이름 & 제네릭 메서드 이름 중복 가능

 ex) 위 내용에 대한 예제 모음

        // 속성 Value는 형식 매개변수 T를 사용하기만 함
        public struct Nullable<T>
        {
            public T Value { get; }
        }
        
        // 배의 인스턴스들의 값을 기본 값으로 초기화하는 메서드
        public static void Zap<T>(T[] array)
        {
            for (int i = 0; i < array.Length; i++)
                array[i] = default(T); // 기본 값으로 지정
        }
        
        // 여러 개의 형식 매개변수가 도입된 제네릭 형식
        class Dictionary<TKey, TValue> { ... }

        public static void Main()
        {
            Dictionary<int, string> myDic = new Dictionary<int, string>();
            var myDic2 = new Dictionary<int, string>();
        }
        
        // 제네릭 형식 이름 & 제네릭 메서드 이름 중복가능
        class A { }
        class A<T> { }
        class A<T1, T2> { }

4) 제네릭 제약

어떤 형식도 대입할 수 있는 형식 매개변수에 제약 조건을 지정해 제한

 

where T : (기반클래스명) 기반 클래스 제약 조건 ( 파생 클래스 가능 )
where T : (인터페이스명) 인터페이스 제약 조건 ( 인터페이스 구현 강제 )
where T : class 참조 형식 제약 조건
where T : struct 값 형식 제약 조건 ( 널 가능 형식은 제외 )
where T : new() 매개변수 없는 생성자 제약 조건 ( 매개변수가 없는 공용 생성자가 있어야 함 )
where U : T 형식 매개변수가 다른 매개변수와 같은 형식이거나 파생 형식이여야 함

 

ex) GenericClass<T, U>에서  T는 SomeClass이거나 SomeClass의 파생된 Interface1을 구현하는 방식이여야 하고, U는 매개변수가 없는 생성자를 제공해야 함

class SomeClass {}
interface Interface1 {}

class GenericClass<T, U> where T : SomeClass, Interface1
                         where U : new()
{ ... }

 

ex) 제네릭 형식을 파생할 경우와 형식 매개변수를 닫거나 여는 예제

class Stack<T> { ... }
class List<T> { ... }

// 형식 매개변수를 열린 형식으로 파생
class SpecialStack<T> : Stack<T> { ... }

// 형식 매개변수를 닫힌 형식으로 파생 (int)
class IntStack<int> : Stack<T> { ... }

// 파생 클래스에서 새로운 형식 매개변수 도입 (제네릭 이름 변경 가능)
class KeyedList<TElement, TKey> : List<TElement> { ... }

 

 ex) 정적 자료 멤버는 각각 닫힌 형식마다 고유함을 나타내는 예제

    class Bob<T> { public static int Count;  }

    public static void Main()
    {
        Console.WriteLine(++Bob<int>.Count); // 1
        Console.WriteLine(++Bob<int>.Count); // 2
        Console.WriteLine(++Bob<string>.Count); // 1
        Console.WriteLine(++Bob<object>.Count); // 1
        Console.WriteLine(++Bob<int>.Count); // 3
    }

 

참고도서) C# 6.0 완벽가이드

728x90