יצא לנו להכיר את הExtension Methods שיש בLINQ.
חלק מהExtension Methods יודעות לבצע אופטימיזציות במידה והIEnumerable
ראינו, למשל, בטיפ מספר 15, שCount() ממומש ע”י גישה פשוטה לProperty בשם Count, במידה והIEnumerable
אולי זה מפתיע, אבל ישנו מקרה בו אופטימיזציות אלה גורמות להתנהגות מעט מוזרה.
המקרה הוא הExtension Method של Contains. זה נראה כמו Extension Method סטנדרטי:
|
|
יש כאן אופטימיזציה: אם הטיפוס הוא ICollection<TSource>, פשוט מבצעים הסבה וקוראים לContains. אלא שהדבר הזה יכול לגרור התנהגות לא ברורה:
אם נניח נריץ את הקוד הבא:
|
|
לכאורה תקין. האוסף אמנם לא מכיר את המחרוזת הLower case, אבל הוא מכיל את המחרוזת "Jason", ולכן אולי אפשר להניח שזה הגיוני.
קונספטואלית זה דווקא לא הגיוני. הרי Contains הוא Extension Method של IEnumerable<T> ולכן היינו מצפים שהוא בודק האם האיבר קיים כאיבר המתקבל בריצה על הEnumerable באמצעות הComparerהדיפולטי. אם היינו רוצים לציין לו EqualityComparer שהוא ישתמש בו, היינו משתמשים בOverload השני המקבל EqualityComparer.
ניתן לנסות לשכנע את עצמנו שזו כן ההתנהגות שהיינו רוצים, אבל הטיעון הבא לדעתי משכנע מאוד:
נניח שהEnumerable באמת מכיל את המחרוזת הLower case. אז היינו מצפים שגם הEnumerable הבא יכיל אותה:
|
|
אלא שלא. כי הוא באמת משווה את האיברים כאיברים שמתקבלים ע"י ריצה על Enumerable.
את הבעיה הזו מצא Jon Skeet כשהוא ניסה לממש בעצמו את כל כל הפונקציות מLINQ, וראה שהUnit Testים שלו עוברים במקום שאלה של System.Linq נכשלים.
סופ"ש שמכיל דברים כאיברים המתקבלים בריצה על Enumerable טוב.