A. 인덱서(Indexer)의 선언
인덱서(Indexer)는 인덱스를 이용해서 객체 내의 데이터에 접근하게 해주는 프로퍼티이며, 프로퍼티와 인덱서의 차이점은 인덱스를 사용하여 객체를 배열처럼 사용하게 해주는것이다.
아래는 인덱서의 선언 예시이며, MyList는 내부에 정수형식 배열을 갖고, 인덱서를 통해 이 배열에 접근한다. 인덱서를 통해 데이터를 저장하고자 하는 시도가 이뤄질때 지정한 인덱스보다 배열의 크기가 작다면 인덱스에 맞춰 배열의 크기를 재조정한다.
class MyList
{
private int[] array;
public MyList()
{
array = new int[3];
}
public int this[int index] //인덱서
{
get
{
return array[index];
}
set
{
if(index >= array.Length)
{
Array.Resize<int>(ref array, index+1);
Console.WriteLine("Array Resized :{0}",array.Length);
}
array[index] = value;
}
}
public int Length
{
get{ return array.Length; }
}
}
class MainApp
{
static void Main(string[] args)
{
MyList list = new MyList();
for(int i=0; i<5; i++) //Array Resized : 4, Array Resized : 5 가 출력된다
list[i]=i; //배열을 다루듯 인덱스를 통해 데이터를 입력한다.
for(int i=0;i<list.Length;i++) //데이터를 얻어올때도 인덱스를 이용한다.
Console.WriteLine(list[i]); // 0 1 2 3 4
}
}
//????????
B. foreach가 가능한 객체 만들기
foreach문은 아무 형식의 객체에서는 사용할 수 없으며, 배열이나 리스트 같은 컬렉션에서만 사용할 수 있다. 위의 MyList클래스는 foreach문 사용이 불가능하다.
foreach문은 인터페이스 IEnumberable과 IEnumerator를 상속하는 형식만 지원하므로 MyList클래스에 두가지 인터페이스를 상속하고 인터페이스 내의 메소드를 구현하면 foreach문을 사용할 수 있게된다.
( ※ Enumerable : 열거가능한 , Enumerator : 열거자 )
메소드 | 설명 |
IEnumerator GetEnumerator() //IEnumerable의 유일한 메소드 | IEnumerator 형식의 객체를 반환 |
GetEnumerator() 메소드 구현을 위해서는 현재 메소드(GetEnumerator())의 실행을 일시정지 시켜놓고 호출자에게 결과를 반환하는 yield return문을 사용해야한다.
메소드가 다시 호출되면, 일시 정지된 실행을 복구하여 yield return 또는 yield break문을 만날때까지 나머지작업을 실행한다.
// 이 코드에서 yield를 사용하는이유는 이해가 안되므로, 다시 확인필요
class MyEnumerator
{
int[] numbers={1,2,3,4};
public IEnumerator GetEnumerator()
{
yield return numbers[0];
yield return numbers[1];
yield return numbers[2];
yield break; //yield break는 GetEnumerator() 메소드를 종료시킨다.
yield return numbers[3]; //따라서, 이코드는 실행되지 않는다.
}
}
class MainApp
{
static void Main(String[] args)
{
var obj=new MyEnumerator();
foreach (int i in obj)
Console.WriteLine(i);
}
}
또한, GetEnumerator() 메소드는 IEnumerator 형식의 객체, 즉 IEnumerator 인터페이스를 상속하는 클래스의 객체를 반환하면된다. 다음은 IEnumerator 인터페이스의 메소드 및 프로퍼티 목록이다.
메소드 또는 프로퍼티 | 설명 |
boolean MoveNext() | 다음 요소로 이동한다. 컬렉션의 끝을 지난 경우에는 false, 이동이 성공한 경우에는 true를 반환한다. |
void Reset() | 컬렉션의 첫 번째 위치의 "앞"으로 이동한다. 첫 번째 위치가 0번이라면, Reset()을 호출하면 -1번으로 이동한다. 첫 번째 위치로의 이동은 MoveNext()를 호출한 다음에 이뤄진다. |
Object Current {get;} | 컬렉션의 현재 요소를 반환한다. |
class MyList : IEnumerable, IEnumerator
{
private int[] array;
int position = -1; //컬렉션의 현재 위치를 다루는 변수이며 초기값은 0이아닌 -1이다. 0은 배열의 첫번째 요소를
//가리키는 수.
//position이 이 값(0)을 갖고있을때 foreach문이 첫 번째 반복을 수행하면 MoveNext()
//메소드를 실행하고, 이때 position이 1이되어 두번째 요소를 가져오는 문제가 생긴다.
public MyList()
{
array = new int[3];
}
public int this[int index]
{
get { return array[index]; }
set
{
if (index >= array.Length)
{
Array.Resize<int>(ref array, index + 1);
Console.WriteLine($"Array Resized : {array.Length}");
}
array[index] = value;
}
}
//IEnumerable 멤버
public object Current
{
get
{
return array[position];
}
}
//IEnumerator 멤버
public bool MoveNext()
{
if(position == array.Length - 1)
{
Reset();
return false;
}
position++;
return (position < array.Length);
}
//IEnumerator 멤버
public void Reset()
{
position = -1;
}
//IEnumerable 멤버
public IEnumerator GetEnumerator()
{
for(int i=0;i<array.Length;i++)
{
yield return (array[i]); // yield를 쓰는이유에 대해 다시 확인필요
}
}
}
class MainApp
{
static void Main(String[] args)
{
MyList list = new MyList();
for (int i = 0; i < 5; i++)
list[i] = i;
foreach (int e in list)
Console.WriteLine(e);
}
}
메모 : yield의 사용법, 사용하는이유 재확인필요
'C# 공부 > C# 기본 문법' 카테고리의 다른 글
예외 처리하기 (try~catch문, System.Exception 클래스, 예외 던지기, finally, 사용자 정의 예외클래스, 예외 필터(when), 예외처리의 장점) (0) | 2020.07.31 |
---|---|
일반화(Generic) 프로그래밍 (일반화 메소드/클래스, 형식매개변수 제약, 일반화 컬렉션) (1) | 2020.07.28 |
컬렉션 (ArrayList, Queue, Stack, Hashtable) (0) | 2020.07.26 |
배열 (선언 및 사용, 초기화, System.Array 클래스, 2차원/다차원 배열, 가변배열) (0) | 2020.07.26 |
프로퍼티 - 2 (인터페이스의 프로퍼티, 추상 프로퍼티) (0) | 2020.07.26 |