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>
IList<T>의 주요 멤버
| 멤버 | 설명 |
|---|---|
|
인덱스로 요소를 가져오거나 설정 |
|
요소의 첫 번째 위치(인덱스) 찾기 |
|
지정한 위치에 요소 삽입 |
|
인덱스로 요소 삭제 |
나머지 멤버들, 예를 들면 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>를 지원해. 대표적인 것들만 볼까:
| 컬렉션 | 인덱싱 | 설명 |
|---|---|---|
|
네 | 동적 배열 |
|
네 | 일반 배열도 인덱싱 가능 |
|
네 | 데이터 바인딩용 |
|
네 | 알림이 있는 리스트 |
|
네 | 컬렉션의 기본 클래스 |
주의:
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>를 구현 안 하는지(고유성이 순서나 인덱스보다 중요해서!) 자신 있게 설명할 수 있어.
GO TO FULL VERSION