51. foreach implementation

היום נדבר על איך עובד foreach בC#.

קיים ממשק שנקרא IEnumerable. לממשק זה פונקציה אחת:

1
2
3
4
public interface IEnumerable
{
IEnumerator GetEnumerator();
}

שמחזירה ממשק אחר שנקרא IEnumerator:

1
2
3
4
5
6
7
8
9
10
11
public interface IEnumerator
{
bool MoveNext();
object Current
{
get;
}
void Reset();
}

הממשק הזה מאפשר לנו לעשות 3 פעולות:

  1. להתקדם אחד קדימה (ולקבל תשובה אם הצלחנו להתקדם אחד קדימה)
  2. לראות מה האיבר הנוכחי
  3. לחזור להתחלה

החליטו שכל מי שרוצה שיוכלו לעשות foreach עליו, צריך לממש IEnumerable.

איך זה עובד?

כאשר אנחנו כותבים קוד מהסוג הבא:

1
2
3
4
foreach (Person person in people)
{
// Do stuff
}

מה שקורה באמת זה משהו כזה:

1
2
3
4
5
6
7
IEnumerator enumerator = people.GetEnumerator();
while (enumerator.MoveNext())
{
Person person = (Person) enumerator.Current;
// Do stuff
}

כך שבעצם foreach זה כולה syntactic sugar 😃

עכשיו, אפשר להסביר כל מיני דברים, למשל:

איך הוא יודע שהCollection שלי השתנה באמצע הforeach?

ובכן, למשל בList<T>, הפונקציה GetEnumerator, מחזירה איזשהו IEnumerator שמחזיק reference לList. בנוסף יש לשניהם Member שנקרא Version, שכל פעם שמתבצעת פעולה של שינוי על הרשימה (הוספה/הסרה/עדכון וכו’), הוא גדל ב1.

ואז בכל MoveNext, הEnumerator בודק ששני הVersionים שווים, ובמידה ולא, זורק Exception.

שבוע בר מנייה טוב

שתף