- vừa được xem lúc

IEnumerator và IEnumerable

0 0 13

Người đăng: Phuong Le

Theo Viblo Asia

Phuongne, Th11 04, 2019


Người xưa đã có câu: “class nào có implement từ interface IEnumerable đều có thể sử dụng foreach“. Vậy điều này có phải là sự thật? Và IEnumerator rốt cuộc là gì?

Đầu tiên chúng ta xem KĨ qua cấu trúc của 2 interface này:


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

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

Giải thích sơ qua một chút, giả sử IEnumerable là một mảng chứa các số: 11 12 13 14 15

Như vậy IEnumerator đóng vai trò “con trỏ” trong list này, và lúc khởi tạo thì nó đang trỏ vào vị trí index = 0 tức là số 11, đặt tên con trỏ này là numerator. Trong numerator , Current sẽ trỏ tới số 11, hàm MoveNext() trả về true nếu con trỏ vẫn có thể đi tiếp tới các số 12 13 14 15, false nếu không đi hết mảng.

Minh họa hoạt động của IEnumerator và IEnumerable

Đào sâu hơn về IEnumerator

Đầu tiên, cho một ví dụ để trực quan hơn:

public IEnumerator GetListInt() { yield return 1; yield return 2; yield return 3; int i = 0; yield return 4; yield return 5; yield return 6; } public void Example1() { IEnumerator test = GetListInt(); while (test.MoveNext()) { Debug.Log(test.Current); }
}

Log: 1 2 3 4 5 6

Ở ví dụ trên khi ta gọi MoveNext(), nó sẽ đi từng câu lệnh trong hàm GetListInt() cho tới khi gặp câu lệnh với từ khóa yield return, đẩy giá trị sau yield return (ở ví dụ trên là 1 2 3 4 5 6) vào một biến gọi là IEnumerator là current, đồng thời ghi nhớ vị trí hiện tại và thoát ra ngoài.

Cứ tiếp tục như vậy cho tới khi không MoveNext() được nữa thì thoát ra khỏi vòng lặp.

Ở ví dụ trên có thể xem IEnumerator là con trỏ của một mảng int gồm: 1 2 3 4 5 6

IEnumerable là gì?

Lại ném cho anh em một cái ví dụ:

public IEnumerable GetListInt2() { yield return 1; yield return 2; yield return 3; yield return 4; yield return 5; yield return 6;
} public void Example2()
{ IEnumerable ble = GetListInt2(); IEnumerator tor = ble.GetEnumerator(); while (tor.MoveNext()) { Debug.Log(tor.Current); }
}

Nếu các bạn đọc lại interface của IEnumerable thì có thể thấy hàm GetEnumerator(), hiểu đơn giản nó là một mảng có hỗ trợ sử dụng IEnumerator

“Mình vẫn không hiểu? Giải thích thêm tí đê”


Lại tưởng tượng IEnumerable là một quyển sách, IEnumerator là mảnh giấy đánh dấu trang (đang đọc). Một quyển sách có thể có nhiều mảnh đánh dấu trang, di chuyển một mảnh đánh dấu trang (.MoveNext()) không ảnh hưởng tới quyển sách cũng như các mảnh đánh dấu trang khác.

Việc gọi hàm .GetEnumerator cũng giống như việc bạn đặt thêm một mảnh đánh dấu trang vào trang đầu quyển sách vậy

Các mảnh đánh dấu trang này chỉ được đặt theo chiều đi lên nhé, không khứ hồi.

Tổ hợp IEnumerator, IEnumerable và Foreach

Chắc mọi người đã quá quen với cách sử dụng foreach:

List<int> listInt = new List<int>() { 1, 2, 3, 4, 5 }; foreach (int item in listInt) { Debug.Log(item);
}

Thực tế, compiler sẽ ngầm hiểu:

IEnumerator<int> enumerator = listInt.GetEnumerator(); while (enumerator.MoveNext()) { var item = enumerator.Current; Debug.Log(item);
}

Tất nhiên là compiler sẽ KHÔNG hoàn toàn dịch như trên, mình chỉ ghi lại ý chính phục vụ bài viết.

“Vậy tại sao ở ví dụ đầu lại có hàm sử dụng được với IEnumerator và IEnumerable vậy?”


Compiler đã TỰ tạo một class như List vậy, dùng để xử lý trường hợp iterator method này. Nếu anh em tò mò thì có thể tham khảo cách mà compiler đã xử lý hàm trả về IEnumerator/ IEnumerable ở CSharpInDepth

“Sao không thấy nhắc gì tới hàm Reset của IEnumerator vậy?”


Hiện nay thì mình không thấy ai sử dụng hàm này nữa, cách phổ biến khi muốn reset là tạo lại một IEnumerator bằng cách .GetIEnumerator().

Claims

Bài viết từ 2019 nên chỉ có giá trị tham khảo bạn nhé 😉

Nguồn tham khảo

Bình luận

Bài viết tương tự

- vừa được xem lúc

Các loại tham chiếu Nullable trong C# (Phần 1)

1. Giới thiệu. C# 8.0 giới thiệu kiểu tham chiếu nullable và kiểu tham chiếu non-nullable cho phép bạn đưa ra các lựa chọn quan trọng về thuộc tính cho các biến kiểu tham chiếu:.

0 0 52

- vừa được xem lúc

Command pattern qua ví dụ !

Command pattern là gì . Command pattern khá phổ biến trong C #, đặc biệt khi chúng ta muốn trì hoãn hoặc xếp hàng đợi việc thực hiện một yêu cầu hoặc khi chúng ta muốn theo dõi các hoạt động. Hơn nữa, chúng ta có thể hoàn tác tác chúng. .

0 0 192

- vừa được xem lúc

Hiểu Liskov Substitution Principle qua ví dụ !

Liskov Substitution Principle là gì . Nguyên tắc đóng mở xác đinh rằng các instance của lớp con có thể thay thế được instance lớp cha mà vẫn đảm bảo tính đúng đắn của chương trình.

0 0 36

- vừa được xem lúc

Creating custom Controls Wpf

Introduction. Wpf/winforms provides various controls like Buttons, Textbox, TextBlock, Labels etc.

0 0 55

- vừa được xem lúc

[P1] Chọn công nghệ nào để xây dựng website?

Hiện nay nhu cầu phát triển website, app tăng rất cao do xu hướng "số hóa 4.0" trong và ngoài nước.

0 0 86

- vừa được xem lúc

Kiểu dữ liệu trong C#

Dẫn nhập. Ở bài BIẾN TRONG C# chúng ta đã tìm hiểu về biến và có một thành phần không thể thiếu khi khai báo biến – Đó là kiểu dữ liệu.

0 0 36