Bất cứ ai có thể giải thích IEnumerable và IEnumerator cho tôi? [đóng cửa]


272

Bất cứ ai có thể giải thích IEnumerable và IEnumerator cho tôi?

ví dụ, khi nào sử dụng nó trên foreach? sự khác biệt giữa IEnumerable và IEnumerator là gì? Tại sao chúng ta cần sử dụng nó?


54
Đây là hỗn hợp đặt tên tồi tệ nhất kể từ Java và JavaScript
Matthew Lock

@MatthewLock bạn có thể giải thích ý của bạn là gì không?
David Klempfner

Câu trả lời:


274

ví dụ, khi nào sử dụng nó trên foreach?

Bạn không sử dụng IEnumerable"hơn" foreach. Thực hiện IEnumerablelàm cho việc sử dụng foreach có thể .

Khi bạn viết mã như:

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

nó có chức năng tương đương với văn bản:

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

Theo "chức năng tương đương", ý tôi là đó thực sự là những gì trình biên dịch biến mã thành. Bạn không thể sử dụng foreachtrên baztrong ví dụ này trừ khi baz cụ IEnumerable.

IEnumerablecó nghĩa là bazthực hiện phương pháp

IEnumerator GetEnumerator()

Đối IEnumeratortượng mà phương thức này trả về phải thực hiện các phương thức

bool MoveNext()

Object Current()

Phương thức đầu tiên tiến tới đối tượng tiếp theo trong IEnumerableđối tượng đã tạo ra liệt kê, trả về falsenếu hoàn thành và phương thức thứ hai trả về đối tượng hiện tại.

Bất cứ điều gì trong .Net mà bạn có thể lặp lại qua các dụng cụ IEnumerable. Nếu bạn đang xây dựng lớp của riêng mình và nó không được kế thừa từ một lớp thực hiện IEnumerable, bạn có thể làm cho lớp của mình có thể sử dụng được trong các foreachcâu lệnh bằng cách thực hiện IEnumerable(và bằng cách tạo một lớp liệt kê mà GetEnumeratorphương thức mới của nó sẽ trả về).


15
Tôi nghĩ rằng khi người đăng ban đầu nói "overeach", anh ta có nghĩa là "khi nào tôi nên gọi GetEnumerator () / MoveNext () một cách rõ ràng thay vì sử dụng vòng lặp foreach." Cho những gì nó có giá trị.
mqp

6
Ah, tôi nghĩ bạn đúng. Chà, câu trả lời ẩn trong bài viết của tôi: không thành vấn đề, vì dù sao đó cũng là trình biên dịch. Vì vậy, làm những gì dễ dàng nhất, tức là sử dụng foreach.
Robert Rossney

12
Câu trả lời này không nói lên toàn bộ câu chuyện. Vô số đối tượng không cần thực hiện IEnumerable; họ chỉ cần có một phương thức GetEnumerator trả về một thể hiện của một loại mà lần lượt có một bool MoveNext()phương thức và một Currentthuộc tính của bất kỳ loại nào. Cách tiếp cận "gõ vịt" này được triển khai trong C # 1.0 như một cách để tránh quyền anh khi liệt kê các bộ sưu tập kiểu giá trị.
phoog

3
Với sự mở rộng từ trên xuống và làm việc thông qua Workential: Triển khai IEn Vô số (T), bạn có các nguồn tuyệt vời.
ruedi

4
Đây có phải là loại giống như C ++ iteratorkhông?
Matt G

161

Giao diện IEnumerable và IEnumerator

Để bắt đầu kiểm tra quá trình triển khai các giao diện .NET hiện có, trước tiên chúng ta hãy xem xét vai trò của IEnumerable và IEnumerator. Hãy nhớ rằng C # hỗ trợ một từ khóa có tên foreach cho phép bạn lặp lại nội dung của bất kỳ loại mảng nào:

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

Mặc dù có vẻ như chỉ các kiểu mảng mới có thể sử dụng cấu trúc này, nhưng sự thật của vấn đề là bất kỳ loại nào hỗ trợ một phương thức có tên GetEnumerator () có thể được đánh giá bởi cấu trúc foreach. Để minh họa, hãy làm theo tôi!

Giả sử chúng ta có một lớp 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);
   }
}

Lý tưởng nhất, sẽ thuận tiện khi lặp lại các phần con của đối tượng Garage bằng cách sử dụng cấu trúc foreach, giống như một mảng các giá trị dữ liệu:

// 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();
   }
}

Đáng buồn thay, trình biên dịch thông báo cho bạn rằng lớp Garage không thực hiện một phương thức có tên GetEnumerator (). Phương thức này được chính thức hóa bởi giao diện IEnumerable, được tìm thấy ẩn trong không gian tên System.Collections. Các lớp hoặc cấu trúc hỗ trợ hành vi này quảng cáo rằng họ có thể hiển thị các phần con có chứa cho người gọi (trong ví dụ này, chính từ khóa foreach). Dưới đây là định nghĩa của giao diện .NET tiêu chuẩn này:

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

Như bạn có thể thấy, phương thức GetEnumerator () trả về một tham chiếu đến một giao diện khác có tên System.Collections.IEnumerator. Giao diện này cung cấp cơ sở hạ tầng để cho phép người gọi đi qua các đối tượng bên trong được chứa trong bộ chứa tương thích 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.
}

Nếu bạn muốn cập nhật loại Garage để hỗ trợ các giao diện này, bạn có thể đi đường dài và thực hiện từng phương thức theo cách thủ công. Mặc dù bạn chắc chắn có thể tự do cung cấp các phiên bản tùy chỉnh của GetEnumerator (), MoveNext (), Hiện tại và Đặt lại (), có một cách đơn giản hơn. Vì loại System.Array (cũng như nhiều lớp bộ sưu tập khác) đã triển khai IEnumerable và IEnumerator, bạn chỉ cần ủy thác yêu cầu cho System.Array như sau:

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();
   }
}

Sau khi bạn đã cập nhật loại Garage của mình, bạn có thể sử dụng loại này trong cấu trúc foreach C # một cách an toàn. Hơn nữa, do phương thức GetEnumerator () đã được xác định công khai, người dùng đối tượng cũng có thể tương tác với loại 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);

Tuy nhiên, nếu bạn muốn ẩn chức năng của IEnumerable khỏi cấp đối tượng, chỉ cần sử dụng triển khai giao diện rõ ràng:

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

Bằng cách đó, người dùng đối tượng thông thường sẽ không tìm thấy phương thức GetEnumerator () của Garage, trong khi cấu trúc foreach sẽ thu được giao diện trong nền khi cần thiết.

Được điều chỉnh từ Pro C # 5.0 và .NET 4.5 Framework


1
Câu trả lời tuyệt vời, cảm ơn bạn! Tôi có một câu hỏi mặc dù. Trong ví dụ đầu tiên, bạn lặp qua mảng bằng foreach nhưng sau đó bạn không thể làm điều đó trong ví dụ thứ hai. Đây là vì mảng nằm trong một lớp hay vì nó chứa các đối tượng?
Caleb Palmquist

Cảm ơn bạn đã giải thích tuyệt vời. Câu hỏi duy nhất của tôi là tại sao không chỉ có một phương pháp trong Garageđó carArray? Bằng cách đó, bạn không phải tự thực hiện GetEnumerator, vì Arrayđã làm điều này. Ví dụ:foreach(Car c in carLot.getCars()) { ... }
Nick Rolando

62

Triển khai IEnumerable có nghĩa là lớp của bạn trả về một đối tượng IEnumerator:

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

Triển khai IEnumerator có nghĩa là lớp của bạn trả về các phương thức và thuộc tính cho phép lặp:

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

    public bool MoveNext()...

    public object Current...
}

Dù sao đó cũng là sự khác biệt.


1
Thật tuyệt, điều này giải thích (cùng với các bài đăng khác) làm thế nào để chuyển đổi lớp của bạn thành một lớp mà bạn có thể sử dụng "foreach" để lặp lại nội dung của nó.
Contango

54

Giải thích thông qua tương tự + Walk Walk Code

Đầu tiên là một lời giải thích không có mã, sau đó tôi sẽ thêm nó vào sau.

Giả sử bạn đang điều hành một công ty hàng không. Và trong mỗi chiếc máy bay bạn muốn biết thông tin về những hành khách bay trên máy bay. Về cơ bản bạn muốn có thể "đi qua" máy bay. Nói cách khác, bạn muốn có thể bắt đầu ở ghế trước, và sau đó di chuyển về phía sau máy bay, hỏi hành khách một số thông tin: họ là ai, họ đến từ đâu. Máy bay chỉ có thể làm điều này , nếu nó là:

  1. đếm được, và
  2. nếu nó có một bộ đếm.

Tại sao những yêu cầu này? Bởi vì đó là những gì giao diện yêu cầu.

Nếu đây là tình trạng quá tải thông tin, tất cả những gì bạn cần biết là bạn muốn có thể hỏi từng hành khách trên máy bay một số câu hỏi, bắt đầu từ lần đầu tiên và đi đến cuối cùng.

Không đếm được có nghĩa là gì?

Nếu một hãng hàng không "có thể đếm được", điều này có nghĩa là PHẢI có một tiếp viên hàng không có mặt trên máy bay, công việc duy nhất của họ là đếm - và tiếp viên này PHẢI tính theo một cách rất cụ thể:

  1. Tiếp viên / tiếp viên PHẢI bắt đầu trước hành khách đầu tiên (ở phía trước của mọi người nơi họ giới thiệu an toàn, làm thế nào để mặc áo phao vào vv).
  2. Anh ấy / cô ấy (tức là tiếp viên hàng không) PHẢI "di chuyển tiếp" lên lối đi đến ghế đầu tiên.
  3. Anh ấy / cô ấy sẽ ghi lại: (i) người ngồi trong ghế và (ii) vị trí hiện tại của họ trên lối đi.

Thủ tục đếm

Cơ trưởng của hãng hàng không muốn có một báo cáo về mọi hành khách và khi họ được điều tra hoặc tính. Vì vậy, sau khi nói chuyện với người ngồi ở ghế thứ nhất, tiếp viên / tiếp viên sau đó báo cáo với cơ trưởng, và khi báo cáo được đưa ra, quầy sẽ nhớ vị trí chính xác của anh ta / cô ta ở lối đi và tiếp tục đếm ngay nơi anh ta / cô ta rời đi tắt.

Theo cách này, thuyền trưởng luôn có thể có thông tin về người hiện đang bị điều tra. Bằng cách đó, nếu anh ta phát hiện ra rằng cá nhân này thích Manchester City, thì anh ta có thể dành cho hành khách đó sự đối xử ưu đãi, v.v.

  • Quầy tiếp tục đi cho đến khi anh ta đến cuối máy bay.

Hãy kết hợp điều này với IEnumerables

  • Một vô số chỉ là một bộ sưu tập của hành khách trên máy bay. Luật Hàng không dân dụng - về cơ bản là các quy tắc mà tất cả IEnumerables phải tuân theo. Mỗi khi tiếp viên hàng không đến gặp cơ trưởng với thông tin về người đi đường, về cơ bản chúng tôi sẽ 'nhường' hành khách cho cơ trưởng. Cơ trưởng có thể làm bất cứ điều gì anh ta muốn với hành khách - ngoại trừ sắp xếp lại hành khách trên máy bay. Trong trường hợp này, họ được ưu đãi nếu theo dõi Manchester City (ugh!)

    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)
    

Tóm lược

Nói cách khác, một cái gì đó có thể đếm được nếu nó có một bộ đếm . Và bộ đếm phải (về cơ bản): (i) nhớ vị trí của nó ( trạng thái ), (ii) có thể di chuyển tiếp theo , (iii) và biết về người hiện tại mà anh ta đang làm việc.

Vô số chỉ là một từ ưa thích cho "đếm được". Nói cách khác, vô số cho phép bạn 'liệt kê' (tức là tính).


23

IEnumerable thực hiện GetEnumerator. Khi được gọi, phương thức đó sẽ trả về IEnumerator thực hiện MoveNext, Reset và Current.

Do đó, khi lớp của bạn triển khai IEnumerable, bạn đang nói rằng bạn có thể gọi một phương thức (GetEnumerator) và nhận một đối tượng mới được trả về (một IEnumerator) mà bạn có thể sử dụng trong một vòng lặp như foreach.


18

Việc triển khai IEnumerable cho phép bạn có được một IEnumerator cho một danh sách.

IEnumerator cho phép truy cập tuần tự kiểu truy cập vào các mục trong danh sách, sử dụng từ khóa suất.

Trước khi triển khai foreach (ví dụ trong Java 1.4), cách lặp lại danh sách là lấy một điều tra viên từ danh sách, sau đó yêu cầu nó cho mục "tiếp theo" trong danh sách, miễn là giá trị được trả về như sau mục không phải là null. Foreach chỉ đơn giản thực hiện điều đó một cách ngầm định như một tính năng ngôn ngữ, giống như cách mà khóa () thực hiện lớp Monitor đằng sau hậu trường.

Tôi hy vọng foreach hoạt động trên các danh sách vì chúng triển khai IEnumerable.


Nếu nó cho một danh sách tại sao tôi không thể sử dụng foreach?
prodev42

Vịt .Net gõ câu lệnh foreach, nhưng IEnumerable / IEnumerable <T> là cách thích hợp để nói rằng lớp của bạn có thể được liệt kê.
dùng7116

15
  • Một đối tượng triển khai IEnumerable cho phép người khác truy cập từng mục của nó (bởi một điều tra viên) .
  • Một đối tượng triển khai IEnumerator là việc lặp lại. Đó là vòng lặp trên một đối tượng vô số.

Hãy nghĩ về vô số các đối tượng như danh sách, ngăn xếp, cây cối.


12

IEnumerable và IEnumerator (và các đối tác chung của họ IEnumerable <T> và IEnumerator <T>) là các giao diện cơ bản của việc triển khai iterator trong các bộ sưu tập .Net Framework Class Libray .

IEnumerable là giao diện phổ biến nhất mà bạn sẽ thấy trong phần lớn các mã ngoài kia. Nó cho phép vòng lặp foreach, trình tạo (nghĩ năng suất ) và do giao diện nhỏ của nó, nó được sử dụng để tạo ra sự trừu tượng chặt chẽ. IEnumerable phụ thuộc vào IEnumerator .

Mặt khác, IEnumerator cung cấp giao diện lặp ở mức thấp hơn một chút. Nó được gọi là trình lặp rõ ràng cho phép lập trình viên kiểm soát nhiều hơn đối với chu trình lặp.

Vô số

IEnumerable là một giao diện tiêu chuẩn cho phép lặp qua các bộ sưu tập hỗ trợ nó (trên thực tế, tất cả các loại bộ sưu tập tôi có thể nghĩ đến ngày nay đều thực hiện IEnumerable ). Hỗ trợ trình biên dịch cho phép các tính năng ngôn ngữ như foreach. Nói chung, nó cho phép thực hiện iterator ẩn này .

vòng lặp foreach

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

Tôi nghĩ rằng foreachvòng lặp là một trong những lý do chính để sử dụng giao diện IEnumerable . foreachcó một cú pháp rất ngắn gọn và rất dễ hiểu so với kiểu C cổ điển cho các vòng lặp nơi bạn cần kiểm tra các biến khác nhau để xem nó đang làm gì.

năng suất từ ​​khóa

Có lẽ một tính năng ít được biết đến là IEnumerable cũng cho phép các trình tạo trong C # sử dụng yield returnvà các yield breakcâu lệnh.

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

Trừu tượng

Một kịch bản phổ biến khác trong thực tế là sử dụng IEnumerable để cung cấp các khái niệm trừu tượng. Bởi vì nó là một giao diện rất nhỏ và chỉ đọc, bạn được khuyến khích trưng bày các bộ sưu tập của mình dưới dạng IEnumerable (chứ không phải là List chẳng hạn). Bằng cách đó, bạn có thể tự do thay đổi triển khai của mình mà không vi phạm mã của khách hàng ( ví dụ thay đổi Danh sách thành LinkedList ).

Gotcha

Một hành vi cần lưu ý là trong việc triển khai phát trực tuyến (ví dụ: truy xuất hàng dữ liệu theo hàng từ cơ sở dữ liệu, thay vì tải tất cả các kết quả trong bộ nhớ trước), bạn không thể lặp lại bộ sưu tập nhiều lần. Điều này trái ngược với các bộ sưu tập trong bộ nhớ như Danh sách , nơi bạn có thể lặp lại nhiều lần mà không gặp vấn đề gì. ReSharper, ví dụ, có một kiểm tra mã cho nhiều phép liệt kê có thể có của IEnumerable .

Máy đếm số

Mặt khác, IEnumerator là giao diện phía sau hậu trường, giúp IEnumerble-foreach-Magic hoạt động. Nói đúng ra, nó cho phép các trình vòng lặp rõ ràng.

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

Theo kinh nghiệm của tôi, IEnumerator hiếm khi được sử dụng trong các tình huống phổ biến do cú pháp dài dòng hơn và ngữ nghĩa hơi khó hiểu (ít nhất là với tôi; ví dụ MoveNext () cũng trả về một giá trị, cái tên không gợi ý gì cả).

Ca sử dụng cho IEnumerator

Tôi chỉ được sử dụng IEnumerator nói riêng thư viện (cấp thấp hơn một chút) và các khuôn khổ mà tôi đã được cung cấp IEnumerable giao diện. Một ví dụ là một thư viện xử lý luồng dữ liệu cung cấp một loạt các đối tượng trong một foreachvòng lặp mặc dù dữ liệu phía sau hậu trường được thu thập bằng nhiều luồng tệp và tuần tự hóa khác nhau.

Mã khách hàng

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

Thư viện

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

Thực hiện IEnumerablevề cơ bản có nghĩa là đối tượng có thể được lặp đi lặp lại. Điều này không nhất thiết có nghĩa là nó là một mảng vì có một số danh sách nhất định không thể được lập chỉ mục nhưng bạn có thể liệt kê chúng.

IEnumeratorlà đối tượng thực tế được sử dụng để thực hiện các lần lặp. Nó kiểm soát việc di chuyển từ một đối tượng sang đối tượng tiếp theo trong danh sách.

Hầu hết thời gian, IEnumerable& IEnumeratorđược sử dụng trong suốt như một phần của foreachvòng lặp.


6

Sự khác biệt giữa IEnumerable và IEnumerator:

  • IEnumerable sử dụng IEnumerator trong nội bộ.
  • IEnumerable không biết mục / đối tượng nào đang thực thi.
  • Bất cứ khi nào chúng ta chuyển IEnumerator sang một chức năng khác, nó sẽ biết vị trí hiện tại của vật phẩm / đối tượng.
  • Bất cứ khi nào chúng ta chuyển một bộ sưu tập IEn Countable cho một chức năng khác, nó không biết vị trí hiện tại của vật phẩm / đối tượng (không biết mục nào đang thực thi)

    IEnumerable có một phương thức GetEnumerator ()

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

IEnumerator có một thuộc tính được gọi là Hiện tại và hai phương thức, Reset () và MoveNext () (rất hữu ích để biết vị trí hiện tại của một mục trong danh sách).

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

5

IEnumerable là một hộp chứa Ienumerator. IEnumerable là giao diện cơ sở cho tất cả các bộ sưu tập. vòng lặp foreach có thể hoạt động nếu bộ sưu tập thực hiện IEnumerable. Trong đoạn mã dưới đây, nó giải thích bước có Trình liệt kê riêng của chúng tôi. Trước tiên hãy xác định Lớp mà chúng ta sẽ thực hiện bộ sưu tập.

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

Bây giờ chúng tôi sẽ định nghĩa Lớp sẽ đóng vai trò là bộ sưu tập cho Khách hàng của lớp chúng tôi. Lưu ý rằng nó đang thực hiện giao diện IEnumerable. Vì vậy, chúng ta phải thực hiện phương thức GetEnumerator. Điều này sẽ trả về Enumerator tùy chỉnh của chúng tôi.

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);
    }
}

Bây giờ chúng tôi sẽ tạo ra Enumerator tùy chỉnh của riêng mình như sau. Vì vậy, chúng ta phải thực hiện phương thức 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.
        }
    }

Bây giờ chúng ta có thể sử dụng vòng lặp foreach trên bộ sưu tập của chúng tôi như dưới đây;

    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

Một sự hiểu biết về mẫu Iterator sẽ hữu ích cho bạn. Tôi khuyên bạn nên đọc như vậy.

Mô hình lặp

Ở mức cao, mẫu lặp có thể được sử dụng để cung cấp một cách lặp tiêu chuẩn thông qua các bộ sưu tập thuộc bất kỳ loại nào. Chúng tôi có 3 người tham gia vào mẫu lặp, bộ sưu tập thực tế (máy khách), bộ tổng hợp và bộ lặp. Tập hợp là một lớp giao diện / trừu tượng có một phương thức trả về một trình vòng lặp. Iterator là một lớp giao diện / trừu tượng có các phương thức cho phép chúng ta lặp qua một bộ sưu tập.

Để triển khai mẫu, trước tiên chúng ta cần triển khai một trình vòng lặp để tạo ra một khối cụ thể có thể lặp qua bộ sưu tập (máy khách) sau đó bộ sưu tập (máy khách) thực hiện bộ tổng hợp để trả về một thể hiện của trình vòng lặp trên.

Đây là sơ đồ UML Mô hình lặp

Vì vậy, về cơ bản trong c #, IEnumerable là tổng hợp trừu tượng và IEnumerator là Iterator trừu tượng. IEnumerable có một phương thức GetEnumerator duy nhất chịu trách nhiệm tạo một thể hiện của IEnumerator của loại mong muốn. Các bộ sưu tập như Danh sách thực hiện IEnumerable.

Thí dụ. Giả sử rằng chúng ta có một phương thức getPermutations(inputString)trả về tất cả các hoán vị của một chuỗi và phương thức đó trả về một thể hiện củaIEnumerable<string>

Để đếm số lượng hoán vị, chúng ta có thể làm một cái gì đó như dưới đây.

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

Trình biên dịch c # ít nhiều chuyển đổi ở trên thành

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

Nếu bạn có bất kỳ câu hỏi xin đừng ngần ngại hỏi.


2

Một đóng góp nhỏ.

Như nhiều người trong số họ giải thích về 'khi nào nên sử dụng' và 'sử dụng với foreach'. Tôi đã nghĩ đến việc thêm một sự khác biệt giữa các quốc gia khác ở đây khi được yêu cầu về sự khác biệt giữa cả IEnumerable một IEnumerator.

Tôi đã tạo mẫu mã dưới đây dựa trên các chủ đề thảo luận bên dưới.

IEnumerable, IEnumerator vs foreach, khi nào nên sử dụng sự khác biệt giữa IEnumerator và IEnumerable là gì?

Bộ đếm bảo tồn trạng thái (vị trí lặp) giữa các lệnh gọi hàm trong khi lặp lại mặt khác Số đếm không.

Dưới đây là ví dụ thử nghiệm với ý kiến ​​để hiểu.

Các chuyên gia vui lòng thêm / sửa tôi.

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

Tôi đã nhận thấy những khác biệt:

A. Chúng tôi lặp lại danh sách theo cách khác nhau, foreach có thể được sử dụng cho IEnumerable và vòng lặp while cho IEnumerator.

B. IEnumerator có thể nhớ chỉ mục hiện tại khi chúng ta chuyển từ phương thức này sang phương thức khác (nó bắt đầu làm việc với chỉ mục hiện tại) nhưng IEnumerable không thể nhớ chỉ mục và nó đặt lại chỉ mục để bắt đầu. Thông tin khác trong video này https://www.youtube.com/watch?v=jd3yUjGc9M0


1

IEnumerableIEnumeratorcả hai đều là giao diện trong C #.

IEnumerablelà một giao diện xác định một phương thức duy nhất GetEnumerator()trả về một IEnumeratorgiao diện.

Điều này hoạt động để truy cập chỉ đọc vào một bộ sưu tập thực hiện IEnumerablecó thể được sử dụng với một foreachtuyên bố.

IEnumeratorcó hai phương pháp, MoveNextReset. Nó cũng có một tài sản được gọi là Current.

Sau đây cho thấy việc thực hiện IEnumerable và 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
  */
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.