bdfgdfg

[C#] 인덱서(Indexer) 본문

게임프로그래밍/C#

[C#] 인덱서(Indexer)

marmelo12 2022. 4. 21. 16:25
반응형

C#에서도 연산자 오버로딩이 존재한다.

1
2
3
4
5
6
7
8
9
10
11
12
class MyClass
{
    public int Test { get; set; } = 10;
    public static MyClass operator +(MyClass lhs, MyClass rhs)
    {
        MyClass res = new MyClass();
        res.Test = lhs.Test + rhs.Test;
        return res;
    }
 
}
 
cs

다만 C++과는 달리 C#에서는 []연산자는 오버로딩이 불가능하다.

그렇기에 따로 인덱서라는것을 제공한다.

인덱서는 [] 인덱스 연산자를 이용해서 객체의 데이터에 접근을 하는 프로퍼티

 

예로들어서, 가변배열을 제공하기 위해 C++의 vector 컨테이너를 만들어본다고 해보자.

 -> 참고로 C#에서는 List가 따로 존재한다. (C++의 Vector와 같음.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class MyList
{
    private int[] m_arr = new int[2];
    private int m_size = 0;
    private int m_capacity = 2;
 
    public void Push_Back(int item)
    {
        if(m_size >= m_capacity)
        {
            m_capacity = m_capacity * 2;
            int[] tempArr = new int[m_capacity];
            int push = 0;
            for (; push < m_size; ++push)
                tempArr[push] = m_arr[push];
            tempArr[push] = item;
            m_arr = tempArr;
            ++m_size;
        }
        else
        {
            m_arr[m_size++= item;
        }
    }
    public int Pop_Back()
    {
        return m_arr[--m_size];
    }
 
}
 
 
class Program
{
    static void Main(string[] args)
    {
       
        MyList list = new MyList();
 
        for (int i = 0; i < 10++i)
            list.Push_Back(i);
        for (int i = 0; i < 10++i)
        {
            Console.Write($"{list.Pop_Back()} ");
            
        }
 
    }
}
 
cs

간단하게 만든 가변 배열 클래스.

이제 이 클래스를 통해 만들어진 객체를 임의접근 연산이 가능하게끔 만들어줘야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
// 인덱서 프로퍼티와 똑같다. 다만 이름을 this로 해야한다.
// 대괄호 안의 인덱스명은 아무거나 해도 상관없다.
public int this[int index]
{
    get
    {
        if (index > m_size)
            return -1;
        return m_arr[index];
    }
}
 
cs

되는지 확인해보면.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Program
{
    static void Main(string[] args)
    {
       
        MyList list = new MyList();
 
        for (int i = 0; i < 10++i)
            list.Push_Back(i);
        for (int i = 0; i < 10++i)
        {
            Console.WriteLine(list[i]);
        }
 
    }
}
 
cs

잘 동작한다.

 

다만 매번 저렇게 for문을 선언해서 i = 0, i < 10(여기서는 Length를 반환하는 프로퍼티 혹은 함수가있으면 좋겠다.)를 하는건 귀찮고, foreach로 하면 편할듯하다. 해보면

안되는 이유까지 친절하게 설명해주신다.

 

foreach문은 아무 형식의 객체에서나 사용할수는 없다. 배열이나 리스트 같은 컬렉션에만 사용가능.

 -> 마치 C++의 컨테이너로 치면 어댑터 컨테이너는 반복자가 제공이되지를 않아 범위기반 for문이 불가능한것처럼.

 

foreach구문은 IEnumerable 과 IEnumerator를 상속하는 형식만 지원한다.

 -> 이름에서 알 수 있듯이 인터페이스.

 

IEnumerable 인터페이스가 갖고 있는 메소드는 다음과 같이 하나뿐이다.

IEnumerator GetEnumerator() -> IEnumerator 형식의 객체를 반환.

GetEnumerator함수를 구현할 떄는 yield return문의 도움을 받아야하는데,

yield return문은 현재 GetEnumerator의 실행을 일시 정지시키고 호출자에게 결과를 반환한다.

 

메소드가 다시 호출되면 일시 정지된 실행을 다시 이어서 yield return 또는 yield break문을 만날 때까지 나머지 작업을 처리한다.

 

GetEnumerator 메소드는 IEnumerator 형식의 객체,즉 IEnumerator 인터페이스를 상속하는 클래스의 객체를 반환하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
class MyList : IEnumerable, IEnumerator
{
    private int[] m_arr = new int[2];
    private int m_size = 0;
    private int m_capacity = 2;
 
    private int m_pos = -1// 순회를 하기 위해 필요
 
    //IEnumerator 멤버
    //Current 프로퍼티는 현재 위치의 요소를 반환한다.
    public object Current
    {
        get
        {
            return m_arr[m_pos];
        }
    }
 
    // IEnumerator 멤버
    // 다음 위치의 요소로 이동
    public bool MoveNext()
    {
        if (m_pos == m_size - 1)
        {
            Reset();
            return false;
        }
 
        m_pos++;
        return m_pos < m_size;
    }
    // IEnumerator 멤버 
    //  위치를가리키는 pso를 첫 요소의 '앞'으로 옮긴다
    public void Reset()
    {
        m_pos = -1;
    }
    // IEnumerable 멤버
    public IEnumerator GetEnumerator()
    {
        for (int i = 0; i < m_size; ++i)
            yield return m_arr[i];
    }
 
 
    public void Push_Back(int item)
    {
        if(m_size >= m_capacity)
        {
            m_capacity = m_capacity * 2;
            int[] tempArr = new int[m_capacity];
            int push = 0;
            for (; push < m_size; ++push)
                tempArr[push] = m_arr[push];
            tempArr[push] = item;
            m_arr = tempArr;
            ++m_size;
        }
        else
        {
            m_arr[m_size++= item;
        }
    }
    public int Pop_Back()
    {
        return m_arr[--m_size];
    }
 
    // 인덱서 프로퍼티와 똑같다. 다만 이름을 this로 해
    // 대괄호 안의 인덱스명은 아무거나 해도 상관없다.
    public int this[int index]
    {
        get
        {
            if (index > m_size)
                return -1;
            return m_arr[index];
        }
    }
 
}
 
 
class Program
{
    static void Main(string[] args)
    {
       
        MyList list = new MyList();
 
        for (int i = 0; i < 10++i)
            list.Push_Back(i);
        
        foreach (int score in list)
        {
            Console.WriteLine(score);
        }
 
    }
}
 
cs

 

반응형
Comments