286. ReadOnly enumerable

ראינו בעבר הרחוק (טיפ מספר 4) שניתן לעטוף IListב ReadOnlyCollection כדי שלא יוכלו לשנות אותו.

נניח שיש לנו איזשהו IEnumerable, ואנחנו מעוניינים שלא יוכלו לשנות אותו.

אופציה אחת היא להחזיר אותו מהפונקציה עם טיפוסIEnumerable, אלא שאפשר להתחכם איתנו:

נניח שנחזיר את הCollection כך:

1
2
3
4
5
6
private List<int> mTheNumbers =new List<int>() { 4, 8, 15, 16, 23, 42 };
public IEnumerable<int> GetTheNumbers()
{
return mTheNumbers;
}

אלא שאפשר להתחכם איתנו:

1
2
ICollection<int> numbers = (ICollection<int>)GetTheNumbers();
numbers.Add(108);

מה אפשר לעשות?

אופציה אחת היא לעטוף אותו בReadOnlyCollection. הבעיה היא שReadOnlyCollection יודע לעבוד רק מולIList.

אופציה אחרת היא טיפה יותר יצירתית: נחזיר טיפוס אחר:

אפשר לעשות את זה בשתי דרכים: הדרך הנאיבית היא ליצור מחלקת Wrapper:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ReadOnlyEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerable<T> mSource;
public ReadOnlyEnumerable(IEnumerable<T> source)
{
mSource = source;
}
public IEnumerator<T> GetEnumerator()
{
return mSource.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}

ואז להחזיר ככה:

1
2
3
4
public IEnumerable<int> GetTheNumbers()
{
return new ReadOnlyEnumerable<int>(mTheNumbers);
}

אופציה שנייה היא יותר יצירתית והיא נראית ככה:

1
2
3
4
public IEnumerable<int> GetTheNumbers()
{
return mTheNumbers.Select(x => x);
}

מה קורה כאן? מצד אחד זה נראה כאילו לא קרה כלום. מצד שני, מה שקורה כאן זה שמוחזר IEnumerable<int> עם טיפוס אחר.

הקסם בדרך הזאת היא שגם אם אחר כך יעדכנו את mTheNumbers, יעודכן גם הIEnumerable<int> שחוזר מהפונקציה. וזאת למה? כי המתודות של LINQ הן Lazy – כלומר הן מחושבות רק כאשר אנחנו מתחילים לרוץ על הEnumerable! (ראו גם טיפים מספר 90,91)

בכל אופן, לדעתי זו דרך מאוד מעניינת ושווה להשקיע בלהבין למה היא עובדת…

סוף שבוע שלא ניתן לקריאה בלבד טוב!

שתף