IEnumerable Và IEnumerator Trong C#

Là hai bộ giao diện được định nghĩa sẵn trong không gian tên System.Collections của .NET. Vậy IEnumerable và IEnumerator là gì và có tác dụng như thế nào? Chúng ta sẽ cùng tìm hiểu kỹ hơn về chúng trong bài viết này.

Trong bài viết này, chúng ta sẽ sử dụng một danh sách các số nguyên để làm ví dụ như sau:

List<int> listInt = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

Bộ giao diện IEnumerable là gì và được sử dụng như thế nào?

Mỗi khi chúng ta sử dụng ngôn ngữ lập trình C# để thao tác với danh sách, thường chúng ta cần duyệt qua từng phần tử từ đầu đến cuối như trong đoạn mã dưới đây:

var count = listInt.Count;
for (var i = 0; i < count; i++) 
{
    var item = listInt[i];
    Console.WriteLine("{0}", item);
}

Hoặc

foreach (var item in listInt) 
{
    Console.WriteLine("{0}", item);
}

Với cách làm đầu tiên, đây là một phương pháp duyệt danh sách cổ điển mà hầu như mọi ngôn ngữ lập trình đều hỗ trợ. Chúng ta không sẽ nói về cách làm này trong bài viết này. Với cách làm thứ hai, nhiều ngôn ngữ lập trình hiện đại hỗ trợ việc duyệt danh sách bằng vòng lặp foreach. Ngôn ngữ C# không phải là một ngoại lệ. Vậy làm thế nào C# có thể duyệt danh sách bằng foreach? Câu trả lời là đối tượng đó cài đặt bộ giao diện IEnumerable hoặc IEnumerable<T> (hỗ trợ kiểu dữ liệu chung). Hãy xem định nghĩa của hai bộ giao diện này:

public interface IEnumerable 
{
    IEnumerator GetEnumerator();
}

public interface IEnumerable<out T> : IEnumerable 
{
    IEnumerator<T> GetEnumerator();
}
Có Thể Bạn Quan Tâm :   Nữ Hộ Sinh - 1 trong những công việc cao quý của ngành Y Dược

Trong C#, có nhiều kiểu danh sách như Dictionary, List, ArrayList, HashSet… và ngay cả chuỗi (kiểu dữ liệu string) cũng cài đặt bộ giao diện IEnumerable hoặc IEnumerable<T>.

public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable, IDictionary, ICollection, IReadOnlyDictionary<TKey, TValue>, IReadOnlyCollection<KeyValuePair<TKey, TValue>>, ISerializable, IDeserializationCallback 
{
    //...
}

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T> 
{
    //...
}

public class ArrayList : IList, ICollection, IEnumerable, ICloneable 
{
    //...
}

public class HashSet<T> : ICollection<T>, IEnumerable<T>, IEnumerable, ISerializable, IDeserializationCallback, ISet<T>, IReadOnlyCollection<T> 
{
    //...
}

public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<String>, IEnumerable<char>, IEquatable<String> 
{
    //...
}

Vậy bộ giao diện IEnumerable giúp cho một đối tượng có thể được duyệt bằng vòng lặp foreach. Hãy xem ví dụ dưới đây:

public class MyIntergerCollection : IEnumerable 
{
    private readonly int[] _listInt;
    
    public MyIntergerCollection(int[] listInt) 
    {
        _listInt = listInt;
    }
    
    public IEnumerator GetEnumerator() 
    {
        return _listInt.GetEnumerator();
    }
}

Và khi thực hiện duyệt các phần tử từ đầu đến cuối như sau:

var customMyCollection = new MyIntergerCollection(listInt.ToArray());
foreach (var item in customMyCollection)
{
    Console.WriteLine(item);
}

Kết quả của việc duyệt sẽ giống như việc sử dụng vòng lặp for hoặc foreach thông thường:

1 2 3 4 5 6 7 8 9 10

Bộ giao diện IEnumerator là gì và có tác dụng như thế nào?

Hãy chú ý đến định nghĩa của bộ giao diện IEnumerable, nơi mà hàm GetEnumerator() trả về một đối tượng được cài đặt từ bộ giao diện IEnumerator. Hãy xem định nghĩa của bộ giao diện này:

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

public interface IEnumerator<out T> : IDisposable, IEnumerator 
{
    T Current { get; }
}
Có Thể Bạn Quan Tâm :   Xây dựng dân dụng là gì? Phân cấp công trình dân dụng

Trong đó:Hàm Current: trả về phần tử hiện tại của danh sách đang duyệt.Hàm MoveNext(): trả về true nếu có thể duyệt đến phần tử tiếp theo, ngược lại trả về false.Hàm Reset(): thiết lập lại vị trí duyệt ban đầu.Với định nghĩa trên, chúng ta có thể khẳng định rằng bộ giao diện IEnumerator giúp chúng ta duyệt qua danh sách như thế nào? Mặc định, .NET cho phép chúng ta duyệt từ đầu đến cuối của danh sách. Trong ví dụ trên, chúng ta sử dụng hàm _listInt.GetEnumerator() (vì mảng cũng cài đặt bộ giao diện IEnumerable) để trả về đối tượng tương ứng. Giờ chúng ta hãy tạo một cách duyệt tùy chỉnh có kết quả tương tự.

public class MyIntergerEnumerator : IEnumerator 
{
    private readonly int[] _listInt;
    private int _currentIndex = -1;
    
    public MyIntergerEnumerator(int[] listInt) 
    {
        _listInt = listInt;
    }
    
    public object Current 
    { 
        get 
        {
            return _listInt[_currentIndex];
        }
    }
    
    public bool MoveNext() 
    {
        _currentIndex++;
        return _currentIndex < _listInt.Length;
    }
    
    public void Reset() 
    {
        _currentIndex = -1;
    }
}

Và đoạn mã ví dụ trên sẽ thay thế cho đoạn mã cũ như sau:

public class MyIntergerCollection : IEnumerable 
{
    private readonly int[] _listInt;
    
    public MyIntergerCollection(int[] listInt) 
    {
        _listInt = listInt;
    }
    
    public IEnumerator GetEnumerator() 
    {
        return new MyIntergerEnumerator(_listInt);
    }
}

Sau khi đọc đến đây, có thể bạn sẽ tự hỏi tại sao chúng ta phải làm như thế này (viết thêm hai lớp mới MyIntergerCollection và MyIntergerEnumerator) trong khi .NET đã hỗ trợ sẵn? Thực tế, bằng cách viết như vậy, chúng ta có thể:- Tạo ra một kiểu danh sách mới có thể duyệt bằng foreach- Điều chỉnh quá trình duyệtNhư ví dụ ở trên, chúng ta có thể thấy rằng việc duyệt qua danh sách bắt đầu từ phần tử đầu tiên đến phần tử cuối cùng. Bây giờ, hãy thử với một trường hợp duyệt từ phần tử cuối cùng đến phần tử đầu tiên. Chúng ta chỉ cần điều chỉnh lớp MyIntergerEnumerator như sau:

public class MyIntergerEnumerator : IEnumerator 
{
    private readonly int[] _listInt;
    private int _currentIndex;
    
    public MyIntergerEnumerator(int[] listInt) 
    {
        _listInt = listInt;
        _currentIndex = _listInt.Length;
    }
    
    public object Current 
    { 
        get 
        {
            return _listInt[_currentIndex];
        }
    }
    
    public bool MoveNext() 
    {
        _currentIndex--;
        return _currentIndex >= 0;
    }
    
    public void Reset() 
    {
        _currentIndex = _listInt.Length;
    }
}

Và kết quả của chương trình sẽ là:

10 9 8 7 6 5 4 3 2 1

Thật thú vị phải không?

Có Thể Bạn Quan Tâm :   Game đối kháng là gì? Top 3 game đối kháng hay nhất hiện nay

Kết luận:

Qua bài viết này, chúng ta đã làm quen với bộ giao diện IEnumerable và IEnumerator. Hy vọng những kiến thức này sẽ giúp ích cho bạn trong công việc và học tập của mình.

You May Also Like

About the Author: admin