IEnumerable은 컬렉션(배열, 리스트 등)에서 요소를 순차적으로 열거(iterate)할 수 있도록 해주는 인터페이스이다.
즉,
foreach문을 사용할 수 있도록 하는 역할을 한다.
foreach 사용 가능)[]) 접근을 제공하지 않는다. (리스트처럼 myList[0] 사용 불가)IEnumerator를 반환하는 GetEnumerator() 메서드를 가지고 있다.IEnumerable<T>를 사용하면 제네릭 타입을 지원한다.IEnumerable과 IEnumerator의 관계IEnumerable은 컬렉션을 열거할 수 있는 기능을 제공하는 인터페이스이다.IEnumerable의 GetEnumerator() 메서드는 IEnumerator 객체를 반환한다.IEnumerator는 MoveNext(), Current, Reset() 메서드를 제공하여 컬렉션을 하나씩 순회할 수 있도록 한다.IEnumerable 기본 사용법컬렉션에서 IEnumerable 사용 (List 등)
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
// IEnumerable<int>를 사용하여 foreach 문 사용 가능
IEnumerable<int> enumerable = numbers;
foreach (var num in enumerable)
{
Console.WriteLine(num); // 1, 2, 3, 4, 5 출력
}
}
}
List<T>는 기본적으로 IEnumerable<T>를 구현하고 있어서 foreach 문을 사용할 수 있다.IEnumerable직접 구현하기IEnumerable을 직접 구현하면 사용자 정의 컬렉션에서도 foreach문을 사용할 수 있다.
사용자 정의 IEnumerable 컬렉션 구현
using System;
using System.Collections;
using System.Collections.Generic;
public class MyCollection<T> : IEnumerable<T>
{
private List<T> items = new List<T>();
public void Add(T item) => items.Add(item);
public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); // 비제네릭 버전
}
class Program
{
static void Main()
{
MyCollection<int> numbers = new MyCollection<int>();
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);
foreach (var num in numbers)
{
Console.WriteLine(num); // 1, 2, 3 출력
}
}
}
IEnumerator를 직접 구현하는 방법IEnumerable을 직접 구현하는 또 다른 방법으로 IEnumerator를 수동으로 구현할 수 있다.
using System;
using System.Collections;
using System.Collections.Generic;
// 사용자 정의 컬렉션 클래스 (IEnumerable<T> 구현)
public class CustomCollection<T> : IEnumerable<T>
{
private T[] _items; // 컬렉션 요소를 저장할 배열
// 생성자에서 배열을 받아서 저장
public CustomCollection(T[] items) => _items = items;
// IEnumerable<T> 인터페이스 구현
public IEnumerator<T> GetEnumerator() => new CustomEnumerator<T>(_items);
// IEnumerable(비제네릭) 인터페이스 구현 - 제네릭 버전의 GetEnumerator()를 호출
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
// 사용자 정의 열거자 클래스 (IEnumerator<T> 구현)
public class CustomEnumerator<T> : IEnumerator<T>
{
private T[] _items; // 컬렉션 데이터를 저장하는 배열
private int _position = -1; // 현재 위치 (초기값은 -1, 즉 첫 번째 요소 이전)
// 생성자를 통해 데이터를 받아옴
public CustomEnumerator(T[] items) => _items = items;
// 현재 요소 반환 (IEnumerator<T>에서 요구됨)
public T Current => _items[_position];
// IEnumerator 인터페이스에서 Current 속성을 구현 (비제네릭 버전)
object IEnumerator.Current => Current;
// 다음 요소로 이동 (이동 가능하면 true 반환, 끝이면 false 반환)
public bool MoveNext()
{
_position++; // 위치 증가
return _position < _items.Length; // 배열 길이를 초과하면 false 반환
}
// 반복기 초기화 (위치를 처음으로 리셋)
public void Reset() => _position = -1;
// 자원 정리 (현재는 필요 없음, 빈 메서드)
public void Dispose() { }
}
// 실행 예제
class Program
{
static void Main()
{
// CustomCollection<int> 인스턴스 생성
CustomCollection<int> collection = new CustomCollection<int>(new int[] { 10, 20, 30 });
// foreach문을 통해 자동으로 GetEnumerator()가 호출됨
foreach (var item in collection)
{
Console.WriteLine(item); // 10, 20, 30 출력
}
}
}
yield return을 사용하면 간단하게 IEnumerable을 구현할 수 있다!
using System;
using System.Collections.Generic;
public class NumberGenerator
{
public static IEnumerable<int> GetNumbers()
{
yield return 1;
yield return 2;
yield return 3;
}
}
class Program
{
static void Main()
{
foreach (var num in NumberGenerator.GetNumbers())
{
Console.WriteLine(num); // 1, 2, 3 출력
}
}
}
yield return을 사용하면 상태 관리 없이도 간단하게 IEnumerable을 반환할 수 있다!| 개념 | 설명 |
|---|---|
IEnumerable<T> | 메모리에 로드된 컬렉션을 순회할 때 사용 (즉시 실행) |
IQueryable<T> | 쿼리를 데이터베이스로 변환하여 실행 (지연 실행) |
| LINQ 지원 여부 | 둘 다 가능하지만, IQueryable<T>는 SQL로 변환 가능 |
| 개념 | 설명 |
|---|---|
| IEnumerable | 컬렉션을 순회할 수 있도록 하는 인터페이스 (foreach 지원) |
IEnumerable<T> | 특정 타입의 컬렉션을 순회할 수 있도록 하는 제네릭 인터페이스 |
IEnumerator | 컬렉션을 하나씩 이동(MoveNext()), 현재 요소 반환(Current)하는 인터페이스 |
yield return | IEnumerable을 간편하게 구현하는 방법 |
IQueryable과 차이 | IEnumerable은 메모리 내 컬렉션 순회, IQueryable은 SQL 변환 가능 |
IEnumerable을 구현하면 컬렉션을 foreach 문에서 사용할 수 있다.IEnumerator를 활용하면 컬렉션을 수동으로 순회하는 방법도 가능하다.yield return을 사용하면 간단하게 IEnumerable을 구현할 수 있다.IQueryable과 차이점도 이해하면 LINQ 사용 시 어떤 걸 선택할지 결정할 수 있다.