رابط های IEnumerator و IEnumerable

تمامی کلاسهایی که به نحوی شامل یک Collection هستند این دو رابط رو پیاده سازی می کنند . وجود IEnumerable که توسط کلاسها پیاده سازی می شود به کلاس این امکان را می دهد که بصورت ضمنی و توکار بشود شیء را پیمایش کرد. دقیقا به همین دلیل می توان با استفاده از حلقه foreach یک آرایه را پیمایش کرد، چون که رابط IEnumerable توسط کلاس System.Array پیاده سازی می شود. رابط IEnumerator در سطح پایین تری از یک IEnumerable قرار دارد. با استفاده از این رابط می توان در هرجای بدنه متد اشیایی رو برگشت دهیم بدون اینکه مجبور باشیم ابتدا نتایج را مثلا در یک آرایه بریزیم و بعد آن آرایه رو برگشت دهیم.

 

رابط IEnumerator

یک شمارنده(enumerator) رابط IEnumerator را پیاده سازی می کند که دارای دو متد ()MoveNext() ، Reset و یک خاصیت به نام Current می باشد. خاصیت Current عنصر جاری یک مجموعه را بر می گرداند. این خاصیت یک خاصیت فقط خواندنی (read-only) است و چیزی که برمی گرداند از نوع object می باشد. متد ()MoveNext متدی است که، مکان شمارنده را از یک آیتم در مجموعه به آیتمی دیگر منتقل می کند. این متد یک مقدار بولی را بر می گرداند که نشان می دهد که آیا مکان دیگری برای خواندن در دسترس است یا به انتهای مجموعه رسیده است. اگر مکان جدیدی وجود داشته باشد مقدار true و در غیر اینصورت مقدار false را بر می گرداند. مکان اولیه شمارنده قبل از اولین آیتم مجموعه است، بنابراین ()MoveNext باید قبل از اولین دسترسی خاصیت Current فراخوانی شود. متد ()Reset متدی برای برگرداندن شمارنده به موقعیت اولیه خود قبل از جابجایی است .به تعبیری دیگر، موقعیت اولیه مجموعه را برمی گرداند . با در اختیار داشتن یک شمارنده(enumerator) شما قادر خواهید بود که حلقه foreach را شبیه سازی کرده و عناصر یک مجموعه را با استفاده ازمتد ()MoveNext و خاصیت Current پیمایش کنید.برای درک بهتر عملکرد دو متد و خاصیت مذکور به شکل و کد زیر توجه کنید :
IEnumerable

int[] numbers = { 10, 11, 12, 13 };

IEnumerator IEnumerator1 = numbers.GetEnumerator();
IEnumerator1.MoveNext();
int i = (int)IEnumerator1.Current;

Console.WriteLine(i.ToString());
10

همانطور که در کد بالا مشاهده می کنید متد ()GetEnumerator آرایه numbers را به نوع شمارش پذیر تبدیل می کند، سپس با فراخوانی متد ()MoveNext عدد 10 که اولین عضو آرایه است به عنوان عنصر جاری (Current) بر گردانده می شود. حال فرض کنید که شما می خواهید عدد 12 را چاپ کنید، برای این کار باید متد ()MoveNext را سه با فراخوانی کنید :

int[] numbers = { 10, 11, 12, 13 };

IEnumerator IEnumerator1 = numbers.GetEnumerator();

IEnumerator1.MoveNext();
IEnumerator1.MoveNext();
IEnumerator1.MoveNext();

int i = (int)IEnumerator1.Current;

Console.WriteLine(i.ToString());
12

آرایه ها از انواع قابل شمارش (enumerable) هستند، بنابراین کد زیر روش دستی کاری است که حلقه foreach به صورت خودکار انجام می دهد. در حقیقت کامپایلر #C کدی شبیه به کد زیر را در هنگام نوشتن دستور foreach تولید می کند:

using System;
using System.Collections;

public class Program
{
    public static void Main()
    {
        int[] numbers = { 10, 11, 12, 13 };

        IEnumerator IEnumerator1 = numbers.GetEnumerator(); 
                                                            
        while (IEnumerator1.MoveNext())                     
        {                                                   
            int i = (int)IEnumerator1.Current;              
            Console.WriteLine("{0}", i);                    
        }                                                   
    }
}
10
11
12
13

 

رابط IEnumerable

یک کلاس قابل شمارش (enumerable) کلاسی است که رابط IEnumerable را پیاده سازی کند. رابط IEnumerable فقط یک عضو دارد و آن عضو هم متد ()GetEnumerator می باشد و پارامتر برگشتی این متد از نوع همان رابطIEnumberator است. این رابط در واقع کلاس ما را قابل پیمایش می کند تا بتوانیم حلقه foreach را در مورد کلاسمان بکار ببریم . فرم کلی بصورت زیر است :

using System.Collections;

class MyClass : IEnumerable
{
    public IEnumerator GetEnumerator 
    { 
        ... 
    }
    ...
}

اکنون یک مثال را با هم مرور می کنیم. فرض کنید یک کلاس به نام ColorEnumerator که رابط IEnumerator را پیاده سازی می کند. این کلاس یک رشته از رنگها را در بر می گیرد ، و چون رابط IEnumerator را پیاده سازی می کند، پس قابل پیمایش می شود :

using System;
using System.Collections;
class ColorEnumerator : IEnumerator
{
    string[] Colors;
    int Position = -1;
    public ColorEnumerator(string[] theColors) // Constructor
    {
        Colors = new string[theColors.Length];
        for (int i = 0; i < theColors.Length; i++)
            Colors[i] = theColors[i];
    }

    public object Current                                  // Implement Current.
    {                                    
        get                              
        {                                
            return Colors[Position];     
        }                                
    }                                    
                                                                 
    public bool MoveNext()                                 // Implement MoveNext.
    {                                    
        if (Position < Colors.Length - 1)
        {                                
            Position++;                  
            return true;                 
        }                                
        else                             
            return false;                
    }                                    
                                                                 
    public void Reset()                                    // Implement Reset.
    {                                    
        Position = -1;                   
    }                                    
}

اکنون ما کلاسی دیگر ایجاد می کنیم که رابط IEnumerable را پیاده سازی می کند ، این کلاس در متد ()GetEnumerator پارامتری از نوع کلاس بالا برمی گرداند مطابق شکل زیر :

class MyColors : IEnumerable
{
    string[] Colors = { "Red", "Yellow", "Blue" };
    public IEnumerator GetEnumerator()
    {
        return new ColorEnumerator(Colors);
    }
}

حال در برنامه براحتی می توانیم از حلقه foreach برای پیمایش اعضای آن استفاده کنیم . به کد زیر توجه کنید :

class Program
{
    static void Main()
    {
        MyColors MC = new MyColors();
        foreach (string color in MC)
            Console.WriteLine(color);
    }
}
Red
Yellow
Blue

لطفا اگر نظر، پیشنهاد و یا انتقادی در باره مطلب بالا دارید در قسمت زیر و اگر سوالی دارید در بخش پرسش و پاسخ مطرح بفرمایید.