본문 바로가기
언어 정리/C# 개념 및 lib

제네릭 클래스, Nullable(?), IEnumerator(이터레이터)

by 알 수 없는 사용자 2024. 1. 23.

제네릭 클래스는 C++ 로 따지면 template 이다.

Nullble은 private int? a; 이렇게 선언하면 변수 a 의 자료형이 int 일 수도 null 일 수도 있다는 의미.

IEnumerator를 써서 GetEnumerator라는 함수를 오버라이딩 하면 해당 클래스가 Iterable 하게 된다.

  - Iterable이라면 "foreach (int i in list)" 같이 사용 할 수 있다.

 


https://learn.microsoft.com/ko-kr/dotnet/csharp/fundamentals/types/generics

 

제네릭 클래스 및 메서드 - C#

제네릭에 대해 알아봅니다. 제네릭 형식은 코드 재사용, 형식 안전성 및 성능을 최대화하며 일반적으로 컬렉션 클래스를 만드는 데 사용됩니다.

learn.microsoft.com

 

심플예시

namespace practice;
using System;

// Declare the generic class.
public class GenericList<T>
{
    public void Add(T input) { }
}
class Program
{
    private class ExampleClass { }
    static void Main()
    {
        // Declare a list of type int.
        GenericList<int> list1 = new GenericList<int>();
        list1.Add(1);

        // Declare a list of type string.
        GenericList<string> list2 = new GenericList<string>();
        list2.Add("");

        // Declare a list of type ExampleClass.
        GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
        list3.Add(new ExampleClass());
    }
}

 


Linked List 을 구현 코드

+ Nullable(?) + IEnumerator

namespace practice;
using System;

/* Linked List 을 직접 구현하는 코드 */

public class GenericList<T>
{
    // Linked List 내부에서 다룰 Node class 타입 선언
    private class Node
    {
        private Node? next;         // 다음 Node instance 를 담을 변수
        // ? 의미 : next 변수의 타입이 Node 일수도 null일 수 도 있다.
        // ( 정식 표현은 Nullable 이라고 함 )
        private T data;             // 실 데이터를 넣을 변수

        public Node(T t) // GenericList 클래스의 Node 클래스의 생성자
        {
            next = null;            // 마지막 객체일 시, null 값
            data = t;               // 
        }

        public Node? Next           // Next 함수 getter setter
        {                           // getter : var a = ins.Next; 
            get { return next; }    // setter : ins.Next = 33;
            set { next = value; }
        }

        public T Data
        {
            get { return data; }
            set { data = value; }
        }
    }

    private Node? head; // 다음 Node instance 를 담을 변수

    public T pop_left()             // 가장 왼편의 Linked List pop
    {
        if (head == null)
        {
            throw new InvalidOperationException("This list is empty.");
        }
        T res = head.Data;
        head = head.Next;
        return res;
    }
    
    public T get_left()             // // 가장 왼편의 Linked List getter
    {
        if (head == null)
        {
            throw new InvalidOperationException("This list is empty.");
        }
        return head.Data;
    }

    public GenericList()  // this class의 생성자 
    {                     // 생성자 인자가 없으면 클래스의 디폴트 생성자를 오버라이딩하는 것
        head = null;
    }

    public void AddHead(T t)
    {   // Linked List 구조를 만드는 전신 코드 
        Node n = new Node(t);
        n.Next = head;
        head = n;
    }

    public IEnumerator<T> GetEnumerator()
    {   /* foreach (int i in list) 같은 기능을 사용하려면
           Iterable 해야되는데 그걸 구현 해주는 부분
           정확히는 IEnumerable 인터페이스의 GetEnumerator 함수를 오버라이딩해주는 작업
           */
        Node? current = head;

        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }
}

class Program
{
    static void Main()
    {
        GenericList<int> list = new GenericList<int>();

        for (int x = 0; x < 10; x++)
        {
            list.AddHead(x);
        }

        // var a = list.get_list_once(); // 이전 코드
        // 수정된 메서드 호출
        System.Console.WriteLine($" pop res : {list.pop_left()} "); // 출력값 : pop res : 9
        System.Console.WriteLine($" get res : {list.get_left()} "); // 출력값 : get res : 8
        System.Console.WriteLine($" pop res : {list.pop_left()} "); // 출력값 : pop res : 8

        foreach (int i in list)
        {
            System.Console.Write(i + " ");  // 출력값 : 7 6 5 4 3 2 1 0
        }
        System.Console.WriteLine("\nDone"); // 출력값 : Done
    }
}

댓글