- 컬렉션에 관련된 .NET Framework의 형식들은 크게 다음 세 범주로 나뉜다.
- 표준 컬렉션 프로토콜을 정의하는 인터페이스
- 바로 사용할 수 있는 컬렉션 클래스
- 응용 프로그램에 특화된 커스텀 컬렉션을 작성하는데 사용하는 기반 클래스
- 컬렉션 이름 공간들은 다음과 같다.
이름공간 |
내용 |
System.Collections |
비제네릭 컬렉션 클래스들과 인터페이스들 |
System.Collections.Specialized |
강한 형식의 비제네릭 컬렉션 클래스들 |
System.Collections.Generic |
제네릭 컬렉션 클래스들과 인터페이스들 |
System.Collections.ObjectModel |
커스텀 컬렉션을 위한 프록시들과 기반 클래스들 |
System.Collections.Concurrent |
스레드에 안전한 컬렉션들 |
열거
- 컴퓨팅에는 배열이나 연결 목록 같은 간단한 자료구조에서부터 적흑 트리(red/black tree)나 해시테이블 같은 복잡한 것에 이르기까지 다양한 종류의 컬렉션이 쓰인다.
- 이런 자료구조들의 내부 구현과 외부 특징은 아주 다양하지만, 컬렉션의 내용을 운행하는(traverse) 능력, 다시 말해 컬렉션에 담긴 요소들에 차례로 접근할 수 있는 기능을 제공해야 한다는 점은 거의 보편적이다.
- .NET Framework는 이를 위해 한 쌍의 인터페이스(IEnumerable과 IEnumerator, 그리고 해당 제네릭 인터페이스들)를 제공한다.
- 이들을 구현함으로써 내부 구현과 외부 특징이 서로 다른 자료구조들이라도 공통의 운행 API를 소비자에게 노출할 수 있다.

IEnumerable과 IEnumerator
- 열거자를 대표하는 IEnumerator 인터페이스는 컬렉션의 요소들을 운행하는, 즉 열거하는 기본적인 방법을 규정하는 저수준 프로토콜들을 정의한다.
- 이 인터페이스는 전진(forward) 운행만 지원한다.
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}
- MoveNext 메서드는 현재 요소를 가리키는 ‘커서’를 다음 위치로 옮긴다. 만일 컬렉션에 더 이상의 요소가 없으면 false를 돌려준다.
- Current는 현재 위치의 요소를 돌려준다. (흔히 object에서 좀 더 구체적인 형식으로 캐스팅한다)
- 첫 요소를 조회하기 전에 반드시 MoveNext를 호출해야 한다. 빈(empty) 컬렉션을 허용하려면 이러한 제약이 필요하다.
- Reset 메서드는 커서를 다시 컬렉션의 시작 위치로 되돌린다. 그러면 컬렉션을 다시 연결할 수 있다.
- Reset은 주로 COM과의 상호운용을 위해 존재하는 것이다. 모든 컬렉션이 이 메서드를 구현하지는 않으므로 이를 직접 호출하는 것을 피해야 한다.
- 대체로 컬렉션들은 열거자를 구현하는 것이 아니라, IEnumerable 인터페이스를 통해서 열거자를 제공한다.
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
- 열거자를 돌려주는 메서드 하나를 정의하는 IEnumerable 인터페이스 덕분에 코드의 열거 논리를 다른 클래스에 맡길 수 있다는 유연성이 생긴다.
- 더 나아가서 여러 소비자가 하나의 컬렉션을 서로 간섭하지 않고 열거할 수 있는 능력도 생긴다.
- IEnumeratorProvider 라고 불러도 좋을 이 IEnumerable은 컬렉션 클래스들이 구현하는 가장 기본적인 인터페이스이다.
- 다음은 IEnumerable과 IEnumerator의 저수준 용법을 보여주는 예이다.
string s = "Hello";
// string은 IEnumerable을 구현하므로 GetEnumerator를 호출할 수 있다.
IEnumerator rator = s.GetEnumerator();
while (rator.MoveNext())
{
char c = (char) rator.Current;
Console.Write(c + ".");
}
// output: H.e.l.l.o.
// 보통은 열거자의 메서드를 직접 호출하지 않고 foreach문을 사용한다.
foreach(char c in s)
Console.Write(c + ".");
IEnumerable<T>와 IEumerator<T>
- IEnumerator와 IEnumerable은 거의 항상 그에 해당하는 확장된 제네릭 버전들과 연계해서 구현된다.
- 이 인터페이스들은 Current와 GetEnumerator의 형식 있는 버전들을 정의한다.
- 이 덕분에 정적 형식 안정성이 강해지고, 값 형식 요소의 박싱 비용을 피할 수 있으며, 소비자가 사용하기에도 편하다. 배열은 자동으로 IEnumerable<T>를 구현한다.
public interface IEnumerator<T> : IEnumerator, IDisposable
{
T Current { get; }
}
public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}