누구든지 IEnumerable과 IEnumerator를 설명 할 수 있습니까? [닫은]


272

누구든지 IEnumerable과 IEnumerator를 설명 할 수 있습니까?

예를 들어, foreach를 통해 언제 사용해야합니까? IEnumerable과 IEnumerator의 차이점은 무엇입니까? 왜 사용해야합니까?


55
이것은 자바와 자바 스크립트 이후 최악의 명명 호환 문제입니다
매튜 잠금

@MatthewLock 무슨 뜻인지 설명해 주실 수 있습니까?
David Klempfner

답변:


274

예를 들어, foreach를 통해 언제 사용해야합니까?

IEnumerable"over"를 사용하지 않습니다 foreach. 구현 IEnumerable하면 사용이 foreach 가능해 집니다.

다음과 같은 코드를 작성할 때 :

foreach (Foo bar in baz)
{
   ...
}

기능적으로 다음과 같이 작성합니다.

IEnumerator bat = baz.GetEnumerator();
while (bat.MoveNext())
{
   bar = (Foo)bat.Current
   ...
}

"기능적으로 동등한"이라는 말은 실제로 컴파일러가 코드를 변환하는 것입니다. 당신은 사용할 수 없습니다 foreachbaz이 예에서 않는 baz 구현IEnumerable .

IEnumerable방법을 baz구현하는 수단

IEnumerator GetEnumerator()

IEnumerator이 메소드가 리턴 하는 오브젝트는 메소드를 구현해야합니다.

bool MoveNext()

Object Current()

첫 번째 메서드 IEnumerable는 열거자를 만든 개체의 다음 개체로 진행하여 false완료되면 반환하고 두 번째 메서드는 현재 개체를 반환합니다.

.Net에서 구현을 반복 할 수있는 모든 것 IEnumerable. 자체 클래스를 작성 중이고 구현하는 클래스에서 아직 상속하지 않은 경우 구현하여 (및 새 메소드가 리턴 하는 열거 자 클래스를 작성하여) 클래스를 명령문 IEnumerable에서 사용 가능하게 만들 수 있습니다 .foreachIEnumerableGetEnumerator


15
원래 포스터가 "over foreach"라고 말했을 때 "foreach 루프를 사용하는 대신 명시 적으로 GetEnumerator () / MoveNext ()를 호출해야 할 때"라고 생각했습니다. 그만한 가치가 있습니다.
mqp

6
아, 네 말이 맞아 글쎄, 대답은 내 게시물에 암시 적입니다. 어쨌든 컴파일러가하는 일이기 때문에 중요하지 않습니다. 가장 쉬운 것, 즉 foreach를 사용하십시오.
Robert Rossney

12
이 대답은 전체 이야기를 말하지는 않습니다. 열거 가능한 객체는 IEnumerable을 구현할 필요가 없습니다. 그들은 어떤 유형 의 bool MoveNext()메소드와 Current속성을 갖는 유형의 인스턴스를 리턴하는 GetEnumerator 메소드 만 있으면됩니다 . 이 "덩어리 입력"접근 방식은 C # 1.0에서 구현되어 값 형식 컬렉션을 열거 할 때 권투를 피하는 방법입니다.
phoog

3
위에서 설명하고 Workthrough : IEnumerable (T) 구현을 통해 훌륭한 소스를 얻을 수 있습니다.
ruedi

4
이런 종류의 C ++ iterator입니까?
Matt G

161

IEnumerable 및 IEnumerator 인터페이스

기존 .NET 인터페이스를 구현하는 프로세스를 살펴보기 위해 먼저 IEnumerable 및 IEnumerator의 역할을 살펴 보겠습니다. C #은 모든 배열 유형의 내용을 반복 할 수있는 foreach라는 키워드를 지원합니다.

// Iterate over an array of items.
int[] myArrayOfInts = {10, 20, 30, 40};
foreach(int i in myArrayOfInts)
{
   Console.WriteLine(i);
}

배열 유형 만이 구성을 사용할 수있는 것처럼 보이지만 문제의 진실은 forEach 구성에 의해 평가 될 수있는 GetEnumerator ()라는 메소드를 지원하는 모든 유형입니다.

Garage 클래스가 있다고 가정하십시오.

// Garage contains a set of Car objects.
public class Garage
{
   private Car[] carArray = new Car[4];
   // Fill with some Car objects upon startup.
   public Garage()
   {
      carArray[0] = new Car("Rusty", 30);
      carArray[1] = new Car("Clunker", 55);
      carArray[2] = new Car("Zippy", 30);
      carArray[3] = new Car("Fred", 30);
   }
}

이상적으로는 데이터 값 배열과 마찬가지로 foreach 구문을 사용하여 Garage 객체의 하위 항목을 반복하는 것이 편리합니다.

// This seems reasonable ...
public class Program
{
   static void Main(string[] args)
   {
      Console.WriteLine("***** Fun with IEnumerable / IEnumerator *****\n");
      Garage carLot = new Garage();
      // Hand over each car in the collection?
      foreach (Car c in carLot)
      {
         Console.WriteLine("{0} is going {1} MPH",
         c.PetName, c.CurrentSpeed);
      }
      Console.ReadLine();
   }
}

안타깝게도 컴파일러는 Garage 클래스가 GetEnumerator ()라는 메서드를 구현하지 않음을 알려줍니다. 이 메서드는 System.Collections 네임 스페이스 내에 숨겨져있는 IEnumerable 인터페이스로 공식화됩니다. 이 동작을 지원하는 클래스 또는 구조는 포함 된 하위 항목을 호출자 (이 예제에서는 foreach 키워드 자체)에 노출 할 수 있음을 알립니다. 이 표준 .NET 인터페이스의 정의는 다음과 같습니다.

// This interface informs the caller
// that the object's subitems can be enumerated.
public interface IEnumerable
{
   IEnumerator GetEnumerator();
}

보다시피 GetEnumerator () 메서드는 System.Collections.IEnumerator라는 또 다른 인터페이스에 대한 참조를 반환합니다. 이 인터페이스는 호출자가 IEnumerable 호환 컨테이너에 포함 된 내부 객체를 통과 할 수있는 인프라를 제공합니다.

// This interface allows the caller to
// obtain a container's subitems.
public interface IEnumerator
{
   bool MoveNext (); // Advance the internal position of the cursor.
   object Current { get;} // Get the current item (read-only property).
   void Reset (); // Reset the cursor before the first member.
}

이러한 인터페이스를 지원하도록 차고 유형을 업데이트하려는 경우 시간이 오래 걸리고 각 방법을 수동으로 구현할 수 있습니다. GetEnumerator (), MoveNext (), Current 및 Reset ()의 사용자 정의 버전을 자유롭게 제공 할 수 있지만 더 간단한 방법이 있습니다. System.Array 유형 (및 다른 많은 컬렉션 클래스)은 이미 IEnumerable 및 IEnumerator를 구현하므로 다음과 같이 간단히 요청을 System.Array에 위임 할 수 있습니다.

using System.Collections;
...
public class Garage : IEnumerable
{
   // System.Array already implements IEnumerator!
   private Car[] carArray = new Car[4];
   public Garage()
   {
      carArray[0] = new Car("FeeFee", 200);
      carArray[1] = new Car("Clunker", 90);
      carArray[2] = new Car("Zippy", 30);
      carArray[3] = new Car("Fred", 30);
   }
   public IEnumerator GetEnumerator()
   {
      // Return the array object's IEnumerator.
      return carArray.GetEnumerator();
   }
}

차고 유형을 업데이트 한 후 C # foreach 구문 내에서 해당 유형을 안전하게 사용할 수 있습니다. 또한 GetEnumerator () 메서드가 공개적으로 정의 된 경우 개체 사용자는 IEnumerator 형식과 상호 작용할 수도 있습니다.

// Manually work with IEnumerator.
IEnumerator i = carLot.GetEnumerator();
i.MoveNext();
Car myCar = (Car)i.Current;
Console.WriteLine("{0} is going {1} MPH", myCar.PetName, myCar.CurrentSpeed);

그러나 개체 수준에서 IEnumerable의 기능을 숨기려면 명시 적 인터페이스 구현을 사용하십시오.

IEnumerator IEnumerable.GetEnumerator()
{
  // Return the array object's IEnumerator.
  return carArray.GetEnumerator();
}

그렇게하면 일반 객체 사용자는 Garage의 GetEnumerator () 메소드를 찾지 못하지만 foreach 구문은 필요한 경우 백그라운드에서 인터페이스를 가져옵니다.

Pro C # 5.0 및 .NET 4.5 프레임 워크 에서 채택


1
환상적인 답변, 감사합니다! 그래도 질문이 하나 있습니다. 첫 번째 예에서는 foreach를 사용하여 배열을 반복하지만 두 번째 예에서는 배열을 반복 할 수 없습니다. 배열이 클래스에 있거나 객체를 포함하고 있기 때문입니까?
Caleb Palmquist

좋은 설명 감사합니다. 내 유일한 질문은 왜 그 방법 Garage이없는 것 carArray입니까? 그렇게하면 이미 구현 GetEnumerator했기 때문에 자신 을 구현할 필요가 없습니다 Array. 예foreach(Car c in carLot.getCars()) { ... }
Nick Rolando

63

IEnumerable을 구현하면 클래스에서 IEnumerator 객체를 반환합니다.

public class People : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator()
    {
        // return a PeopleEnumerator
    }
}

IEnumerator를 구현한다는 것은 클래스가 반복을위한 메소드와 속성을 반환한다는 것을 의미합니다.

public class PeopleEnumerator : IEnumerator
{
    public void Reset()...

    public bool MoveNext()...

    public object Current...
}

어쨌든 차이점입니다.


1
이 방법은 다른 게시물과 함께 클래스를 "foreach"를 사용하여 내용을 반복 할 수있는 클래스로 변환하는 방법을 설명합니다.
Contango

54

유추 + 코드 연습을 통한 설명

먼저 코드없이 설명을 한 다음 나중에 추가하겠습니다.

항공사를 운영하고 있다고 가정 해 봅시다. 그리고 각 비행기에서 비행기에 탑승하는 승객에 대한 정보를 알고 싶습니다. 기본적으로 비행기를 "이동"할 수 있기를 원합니다. 다시 말해, 승객은 앞 좌석에서 출발 한 다음 비행기 뒤쪽으로 나아갈 수 있기를 원하며 승객에게 정보를 요청합니다. 승객의 위치, 위치 등 이면 :

  1. 셀 수있는
  2. 카운터가 있으면

왜 이러한 요구 사항이 있습니까? 인터페이스에 필요한 것이기 때문입니다.

이것이 정보 과부하 인 경우, 비행기에서 각 승객에게 처음부터 시작하여 마지막까지 몇 가지 질문을 할 수 있기를 원한다는 것만 알면됩니다.

셀 수있는 것은 무엇을 의미합니까?

항공사가 "가산 성"인 경우, 비행기에 승무원이 있어야하며, 유일한 직원은 계산해야하며,이 승무원은 매우 구체적인 방식으로 계산해야합니다.

  1. 카운터 / 비행 승무원은 첫 승객보다 먼저 출발해야합니다 (안전을 시연하는 모든 사람 앞에서, 구명 조끼 착용 방법 등).
  2. 그 / 그녀 (즉, 승무원)는 통로에서 첫 좌석으로 "다음으로 이동"해야합니다.
  3. 그 후 그녀는 (i) 좌석에있는 사람, (ii) 통로에서 현재 위치를 기록해야합니다.

계산 절차

항공사의 선장은 모든 승객에 대해 조사 또는 계산 시점에 대한 보고서를 원합니다. 따라서 첫 번째 좌석에있는 사람에게 말한 후 승무원 / 카운터는 선장에게보고하고, 보고서가 제공되면 카운터는 통로에서 자신의 정확한 위치를 기억하고 자신이 떠난 곳에서 계속 카운팅합니다. 떨어져서.

이런 식으로 선장은 항상 현재 조사중인 사람에 대한 정보를 가질 수 있습니다. 그렇게하면,이 개인이 맨체스터 시티를 좋아한다는 것을 알게되면, 그 승객에게 우선 치료 등을 줄 수 있습니다.

  • 비행기가 끝날 때까지 카운터는 계속 진행합니다.

이것을 IEnumerables와 묶어 봅시다

  • 열거 가능한 것은 비행기에 승객을 모은 것입니다. 민간 항공법-기본적으로 모든 IEnumerables가 따라야하는 규칙입니다. 항공사 승무원이 Passeger 정보를 사용하여 선장으로 갈 때마다 기본적으로 승객을 선장에게 '수속'합니다. 기장은 기본적으로 승객을 비행기에 다시 배치하는 것을 제외하고는 승객과 함께 원하는 모든 것을 할 수 있습니다. 이 경우 맨체스터 시티를 따를 경우 우선 치료를받습니다 (웃음).

    foreach (Passenger passenger in Plane)
    // the airline hostess is now at the front of the plane
    // and slowly making her way towards the back
    // when she get to a particular passenger she gets some information
    // about the passenger and then immediately heads to the cabin
    // to let the captain decide what to do with it
    { // <---------- Note the curly bracket that is here.
        // we are now cockpit of the plane with the captain.
        // the captain wants to give the passenger free 
        // champaign if they support manchester city
        if (passenger.supports_mancestercity())
        {
            passenger.getFreeChampaign();
        } else
        {
            // you get nothing! GOOD DAY SIR!
        }
    } //  <---- Note the curly bracket that is here!
          the hostess has delivered the information 
          to the captain and goes to the next person
          on the plane (if she has not reached the 
          end of the plane)

요약

즉, 카운터가 있으면 무언가를 계산할 수 있습니다 . 그리고 카운터는 (기본적으로) : (i) 그 장소를 기억하고 ( state ), (ii) 다음 으로 움직일 수 있고 , (iii) 그가 다루고 있는 현재 사람 에 대해 알고 있어야합니다 .

열거 형은 "가산 성"에 대한 멋진 단어입니다. 즉, 열거 가능하면 '열거'(즉, 계산) 할 수 있습니다.


23

IEnumerable 은 GetEnumerator를 구현합니다. 호출되면 해당 메소드는 IEnumerator 를 리턴합니다. MoveNext, Reset 및 Current를 구현 를 .

따라서 클래스에서 IEnumerable을 구현하면 메서드 (GetEnumerator)를 호출하고 foreach와 같은 루프에서 사용할 수있는 새 개체 (IEnumerator)를 반환 할 수 있습니다.


18

IEnumerable을 구현하면 목록에 대한 IEnumerator를 얻을 수 있습니다.

IEnumerator에서는 yield 키워드를 사용하여 각 스타일을 목록의 항목에 순차적으로 액세스 할 수 있습니다.

foreach를 구현하기 전에 (예 : Java 1.4) 목록을 반복하는 방법은 목록에서 열거자를 가져온 다음 다음에 반환 된 값만큼 목록에서 "다음"항목을 ​​요청하는 것입니다. 항목이 null이 아닙니다. foreach는 단순히 lock ()이이면에서 Monitor 클래스를 구현하는 것과 같은 방식으로 언어 기능으로 암시 적으로 수행합니다.

foreach는 IEnumerable을 구현하기 때문에 목록에서 작동합니다.


목록을 보려면 왜 foreach를 사용할 수 없습니까?
prodev42

.Net duck은 foreach 문을 입력하지만 IEnumerable / IEnumerable <T>는 클래스를 열거 할 수있는 적절한 방법입니다.
user7116

15
  • IEnumerable을 구현하는 개체를 사용하면 다른 사람이 각 항목을 열거 자로 열거 할 수 있습니다. .
  • IEnumerator를 구현하는 객체 는 반복을 수행하는 것입니다. 열거 가능한 객체를 반복합니다.

열거 가능한 객체를 목록, 스택, 트리로 생각하십시오.


12

IEnumerable 및 IEnumerator (및 일반적인 대응 요소 인 IEnumerable <T> 및 IEnumerator <T>)는 .Net Framework 클래스 Libray 컬렉션 에서 반복기 구현 의 기본 인터페이스입니다 .

IEnumerable 은 대부분의 코드에서 볼 수있는 가장 일반적인 인터페이스입니다. 그것은 foreach 루프 수, 발전기 (생각 수율 때문에 작은 인터페이스)를하고, 꽉 추상화를 만드는 데 사용됩니다. IEnumerable은 IEnumerator에 따라 다릅니다 .

반면 IEnumerator 는 약간 낮은 수준의 반복 인터페이스를 제공합니다. 프로그래머가 반복주기를보다 잘 제어 할 수 있도록 하는 명시 적 반복 자라고합니다 .

IEnumerable

IEnumerable은이를 지원하는 컬렉션을 반복 할 수있는 표준 인터페이스입니다 (사실, 오늘 생각할 수있는 모든 컬렉션 유형은 IEnumerable을 구현합니다 ). 컴파일러 지원은와 같은 언어 기능을 허용합니다 foreach. 일반적으로이 암시 적 반복자 구현을 가능하게합니다 .

foreach 루프

foreach (var value in list)
  Console.WriteLine(value);

foreach루프가 IEnumerable 인터페이스 를 사용하는 주된 이유 중 하나 라고 생각 합니다 . foreach매우 간결한 구문을 가지고 있으며 고전적인 C에 비해 이해하기 매우 쉽습니다. 다양한 변수를 확인하여 수행중인 작업을 확인 해야하는 루프의 스타일에 .

수익률 키워드

아마도 덜 알려진 기능은 IEnumerable 이 and # 를 사용 하여 C #에서 생성기를 활성화 할 수 있다는 것입니다 .yield returnyield break

IEnumerable<Thing> GetThings() {
   if (isNotReady) yield break;
   while (thereIsMore)
     yield return GetOneMoreThing();
}

추상화

실제로 또 다른 일반적인 시나리오는 IEnumerable 을 사용하여 최소한의 추상화를 제공하는 것입니다. 이 인터페이스는 아주 작고 읽기 전용 인터페이스이므로 컬렉션을 IEnumerable ( 예 : List 대신) 로 노출하는 것이 좋습니다 . 그렇게하면 클라이언트 코드를 손상시키지 않고 구현을 자유롭게 변경할 수 있습니다 (예 : List를 LinkedList 로 변경 ).

잡았다

알아야 할 한 가지 동작은 스트리밍 구현 (예 : 모든 결과를 메모리에 먼저로드하는 대신 데이터베이스에서 행 단위로 데이터 검색) 에서 콜렉션을 두 번 이상 반복 할 수 없다는 것입니다. 이는 List 와 같은 메모리 내 컬렉션과 달리 문제없이 여러 번 반복 할 수 있습니다. 예를 들어 ReSharper 는 IEnumerable의 Multiple Multiple Enumeration에 대한 코드 검사를 수행 합니다.

IEnumerator

반면, IEnumerator는 IEnumerble-foreach-magic 작업을 수행 하는 비하인드 인터페이스입니다 . 엄밀히 말하면 명시적인 반복자를 가능하게합니다.

var iter = list.GetEnumerator();
while (iter.MoveNext())
    Console.WriteLine(iter.Current);

내 경험에 있는 IEnumerator이 거의 인해 일반적인 시나리오에 사용되지는 더 구문 자세한 약간 (적어도 나에게, 예를 들면 의미를 혼동 MoveNext는 () 뿐만 아니라 값을 반환, 이름이 전혀 제안하지 않는다).

IEnumerator의 사용 사례

IEnumerable 인터페이스를 제공하는 특정 (약간 낮은 수준의) 라이브러리 및 프레임 워크 에서만 IEnumerator 를 사용했습니다 . 하나의 예는 다양한 파일 스트림 및 직렬화를 사용하여 장면 뒤에서 데이터를 수집 한 경우에도 일련의 객체를 루프로 제공하는 데이터 스트림 처리 라이브러리입니다 .foreach

클라이언트 코드

foreach(var item in feed.GetItems())
    Console.WriteLine(item);

도서관

IEnumerable GetItems() {
    return new FeedIterator(_fileNames)
}

class FeedIterator: IEnumerable {
    IEnumerator GetEnumerator() {
        return new FeedExplicitIterator(_stream);
    }
}

class FeedExplicitIterator: IEnumerator {
    DataItem _current;

    bool MoveNext() {
        _current = ReadMoreFromStream();
        return _current != null;           
    }

    DataItem Current() {
        return _current;   
    }
}

9

구현 IEnumerable한다는 것은 본질적으로 객체가 반복 될 수 있음을 의미합니다. 인덱스 할 수없는 특정 목록이 있지만 열거 할 수 있으므로 반드시 배열임을 의미하지는 않습니다.

IEnumerator반복을 수행하는 데 사용되는 실제 객체입니다. 목록에서 한 개체에서 다음 개체로의 이동을 제어합니다.

대부분 IEnumerable& IEnumeratorforeach루프의 일부로 투명하게 사용됩니다 .


6

IEnumerable과 IEnumerator의 차이점 :

  • IEnumerable은 내부적으로 IEnumerator를 사용합니다.
  • IEnumerable은 실행중인 항목 / 개체를 모릅니다.
  • IEnumerator를 다른 함수에 전달할 때마다 항목 / 객체의 현재 위치를 알고 있습니다.
  • IEnumerable 컬렉션을 다른 함수에 전달할 때마다 항목 / 객체의 현재 위치를 알 수 없습니다 (실행중인 항목을 모름)

    IEnumerable에는 GetEnumerator () 메서드가 하나 있습니다.

public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

IEnumerator에는 Current라는 속성 하나와 Reset () 및 MoveNext () 메서드가 있습니다 (목록에서 항목의 현재 위치를 아는 데 유용함).

public interface IEnumerator
{
     object Current { get; }
     bool MoveNext();
     void Reset();
}

5

IEnumerable은 열거자를 포함하는 상자입니다. IEnumerable은 모든 컬렉션의 기본 인터페이스입니다. 컬렉션이 IEnumerable을 구현하면 foreach 루프가 작동 할 수 있습니다. 아래 코드에서는 자체 열거자를 갖는 단계를 설명합니다. 먼저 컬렉션을 만들 클래스를 정의 해 봅시다.

public class Customer
{
    public String Name { get; set; }
    public String City { get; set; }
    public long Mobile { get; set; }
    public double Amount { get; set; }
}

이제 클래스 고객을위한 컬렉션으로 작동 할 클래스를 정의하겠습니다. IEnumerable 인터페이스를 구현하고 있습니다. 따라서 GetEnumerator 메소드를 구현해야합니다. 이것은 우리의 커스텀 열거자를 반환합니다.

public class CustomerList : IEnumerable
{
    Customer[] customers = new Customer[4];
    public CustomerList()
    {
        customers[0] = new Customer { Name = "Bijay Thapa", City = "LA", Mobile = 9841639665, Amount = 89.45 };
        customers[1] = new Customer { Name = "Jack", City = "NYC", Mobile = 9175869002, Amount = 426.00 };
        customers[2] = new Customer { Name = "Anil min", City = "Kathmandu", Mobile = 9173694005, Amount = 5896.20 };
        customers[3] = new Customer { Name = "Jim sin", City = "Delhi", Mobile = 64214556002, Amount = 596.20 };
    }

    public int Count()
    {
        return customers.Count();
    }
    public Customer this[int index]
    {
        get
        {
            return customers[index];
        }
    }
    public IEnumerator GetEnumerator()
    {
        return customers.GetEnumerator(); // we can do this but we are going to make our own Enumerator
        return new CustomerEnumerator(this);
    }
}

이제 다음과 같이 고유 한 사용자 지정 열거자를 만듭니다. 따라서 MoveNext 메소드를 구현해야합니다.

 public class CustomerEnumerator : IEnumerator
    {
        CustomerList coll;
        Customer CurrentCustomer;
        int currentIndex;
        public CustomerEnumerator(CustomerList customerList)
        {
            coll = customerList;
            currentIndex = -1;
        }

        public object Current => CurrentCustomer;

        public bool MoveNext()
        {
            if ((currentIndex++) >= coll.Count() - 1)
                return false;
            else
                CurrentCustomer = coll[currentIndex];
            return true;
        }

        public void Reset()
        {
            // we dont have to implement this method.
        }
    }

이제 아래와 같이 컬렉션에 foreach 루프를 사용할 수 있습니다.

    class EnumeratorExample
    {
        static void Main(String[] args)
        {

            CustomerList custList = new CustomerList();
            foreach (Customer cust in custList)
            {
                Console.WriteLine("Customer Name:"+cust.Name + " City Name:" + cust.City + " Mobile Number:" + cust.Amount);
            }
            Console.Read();

        }
    }

4

반복자 패턴을 이해하면 도움이 될 것입니다. 나는 같은 것을 읽는 것이 좋습니다.

반복자 패턴

높은 수준에서 반복자 패턴을 사용하여 모든 유형의 콜렉션을 반복하는 표준 방법을 제공 할 수 있습니다. 반복자 패턴, 실제 콜렉션 (클라이언트), 수집 자 및 반복자에는 3 명의 참가자가 있습니다. 집계는 반복자를 반환하는 메서드가있는 인터페이스 / 추상 클래스입니다. Iterator는 컬렉션을 반복 할 수있는 메서드가있는 인터페이스 / 추상 클래스입니다.

패턴을 구현하려면 먼저 관련 콜렉션 (클라이언트)을 반복 할 수있는 콘크리트를 생성하기 위해 반복자를 구현해야합니다. 그런 다음 콜렉션 (클라이언트)은 수집기를 구현하여 위 반복자의 인스턴스를 리턴합니다.

UML 다이어그램은 다음과 같습니다. 반복자 패턴

따라서 기본적으로 c #에서 IEnumerable은 추상 집계이고 IEnumerator는 추상 반복기입니다. IEnumerable에는 원하는 형식의 IEnumerator 인스턴스를 만드는 단일 메서드 GetEnumerator가 있습니다. Lists와 같은 컬렉션은 IEnumerable을 구현합니다.

예. getPermutations(inputString)문자열의 모든 순열을 반환 하는 메서드 가 있고이 메서드가 인스턴스를 반환 한다고 가정 해 봅시다.IEnumerable<string>

순열 수를 계산하기 위해 다음과 같이 할 수 있습니다.

 int count = 0;
        var permutations = perm.getPermutations(inputString);
        foreach (string permutation in permutations)
        {
            count++;
        }

C # 컴파일러는 위의 내용을 다소 변환합니다.

using (var permutationIterator = perm.getPermutations(input).GetEnumerator())
        {
            while (permutationIterator.MoveNext())
            {
                count++;
            }
        }

궁금한 점이 있으면 언제든지 문의하십시오.


2

작은 기부.

많은 사람들이 '사용시기'와 'foreach와 함께 사용'에 대해 설명합니다. IEnumerable과 IEnumerator의 차이점에 대한 질문에 따라 다른 States Difference를 여기 에 추가하려고 생각했습니다 .

아래 토론 스레드를 기반으로 아래 코드 샘플을 만들었습니다.

IEnumerable, IEnumerator vs foreach, 언제 사용해야 합니까? IEnumerator와 IEnumerable의 차이점은 무엇 입니까?

열거자는 함수 호출 사이의 상태 (반복 위치)를 유지하지만 반복은 열거하지 않습니다.

다음은 이해하기 쉬운 주석이있는 테스트 된 예입니다.

전문가가 저를 추가 / 수정하십시오.

static void EnumerableVsEnumeratorStateTest()
{
    IList<int> numList = new List<int>();

    numList.Add(1);
    numList.Add(2);
    numList.Add(3);
    numList.Add(4);
    numList.Add(5);
    numList.Add(6);

    Console.WriteLine("Using Enumerator - Remembers the state");
    IterateFrom1to3(numList.GetEnumerator());

    Console.WriteLine("Using Enumerable - Does not Remembers the state");
    IterateFrom1to3Eb(numList);

    Console.WriteLine("Using Enumerable - 2nd functions start from the item 1 in the collection");
}

static void IterateFrom1to3(IEnumerator<int> numColl)
{
    while (numColl.MoveNext())
    {
        Console.WriteLine(numColl.Current.ToString());

        if (numColl.Current > 3)
        {
            // This method called 3 times for 3 items (4,5,6) in the collection. 
            // It remembers the state and displays the continued values.
            IterateFrom3to6(numColl);
        }
    }
}

static void IterateFrom3to6(IEnumerator<int> numColl)
{
    while (numColl.MoveNext())
    {
        Console.WriteLine(numColl.Current.ToString());
    }
}

static void IterateFrom1to3Eb(IEnumerable<int> numColl)
{
    foreach (int num in numColl)
    {
        Console.WriteLine(num.ToString());

        if (num>= 5)
        {
            // The below method invokes for the last 2 items.
            //Since it doesnot persists the state it will displays entire collection 2 times.
            IterateFrom3to6Eb(numColl);
        }
    }
}

static void IterateFrom3to6Eb(IEnumerable<int> numColl)
{
    Console.WriteLine();
    foreach (int num in numColl)
    {
        Console.WriteLine(num.ToString());
    }
}

2

나는 다음과 같은 차이점을 발견했다.

A. 우리는리스트를 다른 방식으로 반복합니다. foreach는 IEnumerable에, while 루프는 IEnumerator에 사용될 수 있습니다.

B. IEnumerator는 한 메소드에서 다른 메소드로 전달할 때 현재 색인을 기억할 수 있지만 (현재 색인으로 작업을 시작 함) IEnumerable은 색인을 기억할 수 없으며 색인을 시작으로 재설정합니다. 이 비디오에서 더 많은 https://www.youtube.com/watch?v=jd3yUjGc9M0


1

IEnumerableIEnumerator모두 C에서의 인터페이스이다.

IEnumerable인터페이스 GetEnumerator()를 리턴하는 단일 메소드 를 정의 하는 IEnumerator인터페이스입니다.

이것은 명령문 IEnumerable과 함께 사용할 수있는 구현에 대한 읽기 전용 액세스에 사용됩니다 foreach.

IEnumerator두 가지 방법을 가지고, MoveNext그리고 Reset. 또한라는 속성이 Current있습니다.

다음은 IEnumerable 및 IEnumerator의 구현을 보여줍니다.


0
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Enudemo
{

    class Person
    {
        string name = "";
        int roll;

        public Person(string name, int roll)
        {
            this.name = name;
            this.roll = roll;
        }

        public override string ToString()
        {
            return string.Format("Name : " + name + "\t Roll : " + roll);
        }

    }


    class Demo : IEnumerable
    {
        ArrayList list1 = new ArrayList();

        public Demo()
        {
            list1.Add(new Person("Shahriar", 332));
            list1.Add(new Person("Sujon", 333));
            list1.Add(new Person("Sumona", 334));
            list1.Add(new Person("Shakil", 335));
            list1.Add(new Person("Shruti", 336));
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
           return list1.GetEnumerator();
        }
    }



    class Program
    {
        static void Main(string[] args)
        {
            Demo d = new Demo();  // Notice here. it is simple object but for 
                                //IEnumerator you can get the collection data

            foreach (Person X in d)
            {
                Console.WriteLine(X);
            }

            Console.ReadKey();
        }
    }
}
/*
Output : 

Name : Shahriar  Roll : 332
Name : Sujon     Roll : 333
Name : Sumona    Roll : 334
Name : Shakil    Roll : 335
Name : Shruti    Roll : 336
  */
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.