297. StrongBox

[נכתב ע”י שני אלחרר]

אהלן.

היום אדבר על פתרון נחמד לבעיה הבאה :

לפעמים אנחנו רוצים לעבור על IDictionary ובו”ז לשנות בו את הערכים :

1
2
3
4
5
6
IDictionary<ulong, ulong> dictionary = new Dictionary<ulong, ulong>();
foreach (var kvp in dictionary)
{
dictionary[kvp.Key]++;
}

אך זה לא מתאפשר, כיוון שאי אפשר לשנות את המידע שנמצא מאחורי IEnumerator כשאנחנו מבצעים בו איטרציות. במקרה כזה יזרק לנו InvalidOperationException עם ההודעה :

Collection was modified; enumeration operation may not execute.

במקרה כזה, אני לפעמים משתמש בטיפוס שנקרא StrongBox<T>.

הטיפוס הזה מאפשר לנו לשים בתוכו ערך מסוג T, לעדכן אותו ולתשאל עליו. שימו לב, זה נורא דומה לBoxing של .net, ואפילו יקר יותר מבחינת ביצועים, אך לפעמים זה נוח יותר מאשר ליצור את מבנה הנתונים מחדש או לעדכן אותו בצורה אחרת :

1
2
3
4
foreach (var kvp in dictionary.ToArray())
{
dictionary[kvp.Key]++;
}

שימו לב לטוויסט בעלילה, לקחתי את המילון שבעצם מממש את IEnumerable<KeyValuePair<TKey, TValue>>, והשתמשתי בExtension Method – ToArray כדי לשמור את את המפתחות והערכים של מבנה הנתונים בצד ולעבור עליהן במקום לעבור על מבנה הנתונים בעצמו, כך שאני אוכל לעדכן אותו בזמן שאני עובר "עליו".

הפתרון הזה לא מאפשר לי להיות Thread Safe, כיוון שבזמן שהמתודה ToArray נקראת, מישהו אחר יכול לעדכן לי את המילון – ואז יזרק לי אותו InvalidOperationException מוכר.

אם אבחר להשתמש בStrongBox, את הדוגמה הראשונה אני הופך לכזו :

1
2
3
4
5
6
IDictionary<ulong, StrongBox<ulong>> dictionary = new Dictionary<ulong, StrongBox<ulong>>();
foreach (var kvp in dictionary)
{
dictionary[kvp.Key].Value++;
}

המקרה הזה לא יעיל בפני מחיקות והוספות של ערכים חדשים למילון, אך במידה שאנחנו רק עורכים את המידע שנמצא במילון, הקוד שלנו אפילו Thread Safe.

בברכת ערב קופסא חזקה.

שתף