CodeGym /행동 /C# SELF /인덱스 접근 계약: IList<T><...

인덱스 접근 계약: IList<T>

C# SELF
레벨 28 , 레슨 3
사용 가능

1. 소개

자, 상상해봐: 컬렉션이 있는데... 세 번째, 일곱 번째, 아니면 그냥 0번째 요소를 빠르게 얻고 싶어. 아니면 둘을 바꾸고 싶을 수도 있고. 배열에서는 이게 아주 쉬워 — 인덱스로 (array[3]) 바로 접근하면 돼. 근데 컬렉션에서는 어떨까? 모든 컬렉션이 인덱싱을 지원하는 건 아니잖아!

여기서 IList<T> 인터페이스가 등장해 — 이건 컬렉션이 인덱스로 요소를 다룰 수 있게 해주는 범용 계약이야. 간단히 말하면: 네 컬렉션이 IList<T>를 구현한다면, 인덱스로 (배열처럼) 요소에 접근하고 바로바로 바꿀 수 있다는 뜻이야.

비유:
도서관 카드 생각해봐: 책마다 선반에 자기 번호가 있고, 언제든 "세 번째" 책을 바로 집을 수 있지. IList<T>를 지원하는 컬렉션이 딱 이런 느낌이야.

2. IList<T>의 기본 구조와 메서드

IList<T> 인터페이스는 많은 컬렉션의 주인공이야. 이건 ICollection<T>을 확장하고 (그건 또 IEnumerable<T>을 확장해서, 컬렉션을 foreach로 돌 수 있게 해주지), 거기에 인덱스 작업을 추가해.

인터페이스 상속 구조:


IEnumerable<T>
      ▲
      │
ICollection<T>
      ▲
      │
  IList<T>
.NET 컬렉션 인터페이스 상속 구조

IList<T>의 주요 멤버

멤버 설명
T this[int index] { get; set; }
인덱스로 요소를 가져오거나 설정
int IndexOf(T item)
요소의 첫 번째 위치(인덱스) 찾기
void Insert(int index, T item)
지정한 위치에 요소 삽입
void RemoveAt(int index)
인덱스로 요소 삭제

나머지 멤버들, 예를 들면 Add, Remove, Clear, Contains 등은 ICollection<T>에서 상속받아.

핵심 포인트: 인덱서

IList<T>의 가장 큰 "포인트"는 바로 인덱서야. 이건 문법 설탕(syntactic sugar)이라서, 이렇게 쓸 수 있어:


var myList = new List<int> { 10, 20, 30 };
int secondValue = myList[1]; // 20 가져오기
myList[2] = 42;              // 세 번째 요소 바꾸기

3. 어떤 컬렉션이 IList<T>를 구현할까?

.NET 표준 라이브러리에서 익숙한 구조체들 중 많은 게 IList<T>를 지원해. 대표적인 것들만 볼까:

컬렉션 인덱싱 설명
List<T>
동적 배열
T[]
일반 배열도 인덱싱 가능
BindingList<T>
데이터 바인딩용
ObservableCollection<T>
알림이 있는 리스트
Collection<T>
컬렉션의 기본 클래스

주의:

LinkedList<T>
HashSet<T>
절대 IList<T>를 구현하지 않아. 왜냐면 얘네는 빠른 인덱싱이 없거든 (맞아, LinkedList<T>에는 list[5] 같은 게 없어!).

4. IList<T> 사용 예시

인덱스로 가져오기/설정하기


using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        IList<string> fruits = new List<string> { "사과", "바나나", "배" };

        // 두 번째 요소 가져오기
        string fruit = fruits[1];
        Console.WriteLine(fruit); // 바나나

        // 세 번째 요소 바꾸기
        fruits[2] = "오렌지";
        Console.WriteLine(fruits[2]); // 오렌지
    }
}

인덱스로 삽입/삭제


fruits.Insert(1, "키위");    // 두 번째 자리에 "키위" 삽입
// 리스트: "사과", "키위", "바나나", "오렌지"

fruits.RemoveAt(0);          // 첫 번째 요소("사과") 삭제
// 리스트: "키위", "바나나", "오렌지"

요소 인덱스 찾기


int index = fruits.IndexOf("오렌지"); // 인덱스(2) 반환, 없으면 -1
if (index != -1)
    Console.WriteLine("오렌지는 위치: " + index);
else
    Console.WriteLine("오렌지 없음");

5. 구현 특징과 흔한 실수

IList<T>를 쓸 때, 인덱스가 0부터 시작하고 컬렉션 길이는 현재 요소 개수라는 걸 까먹으면 "함정"에 빠지기 쉬워.

예를 들어, 없는 요소에 접근하려고 하면:


Console.WriteLine(fruits[100]); // IndexOutOfRangeException!

C#에서 인덱스는 달걀처럼 0부터 시작해. 리스트에 4개 있으면 마지막 인덱스는 3이야.

그리고 모든 IList<T> 구현이 똑같이 빠른 건 아니야. 예를 들어 배열이나 List<T>는 인덱스 접근이 즉시(O(1)) 되지만, 만약 네가 직접 연결 리스트로 컬렉션을 만들고 거기에 IList<T>를 붙이면, 인덱스 접근이 느려질 수 있어. (표준 라이브러리는 이렇게 안 해.)

또 한 가지: 배열을 IList<T>로 쓰면 요소는 바꿀 수 있지만, 배열 크기는 못 바꿔. Add, Remove, Insert 같은 메서드를 배열에 쓰면 NotSupportedException이 나와.


int[] myArray = { 1, 2, 3 };
IList<int> listView = myArray; // 업캐스트

listView[0] = 42; // 잘 됨!
listView.Add(99); // NotSupportedException 발생

6. 실전 활용과 왜 필요한가

실제 프로젝트에서 거의 절반은 IList<T>를 구현한 컬렉션이야. 왜냐면 인덱스로 빠르게 접근하고, 바꾸고, 위치 지정해서 삽입/삭제하는 게 너무 편하거든. 예를 들면:

  • WPF나 WinForms의 ViewModel에서 UI 요소 리스트를 바인딩할 때 인터페이스 속성으로 사용.
  • 정렬, 검색, 자리 바꾸기 같은 알고리즘 구현에서 인덱스 접근이 필요할 때.
  • 동적 객체 리스트로 데이터 import/export 모듈 만들 때.

면접에서 IEnumerable<T>, ICollection<T>, IList<T> 차이점 물어보는 건 고전이지. 각 단계가 뭘 담당하는지 알면, 왜 HashSet<T>IList<T>를 구현 안 하는지(고유성이 순서나 인덱스보다 중요해서!) 자신 있게 설명할 수 있어.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION