106. The maybe monad and nested null checking - With extension method

מדי פעם אנחנו נתקלים בבעיה הנפוצה הבאה:

יש לנו אובייקט שאנחנו מעוניינים לגשת לProperty של Property (של Property…) שלו, למשל:

1
ZipCode zipCode = company.Chairman.Address.ZipCode;

(ראינו משהו כזה גם בטיפ 48)

מה הבעיה?

הבעיה היא שיכול להיות שמשהו פה בדרך הוא null.

במקרה כזה, אנחנו בד"כ נתקלים בטיפול כזה:

1
2
3
4
5
6
if ((company != null) &&
(company.Chairman != null) &&
(company.Chairman.Address != null))
{
zipCode = company.Chairman.Address.ZipCode;
}

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

לפעמים אנחנו גם צריכים לטפל במקרים שבהם אחד הדברים בשרשרת הוא null, ולהכניס ערך דיפולטי.

ראינו בעבר את הoperator שהתווסף בC# 2.0 ?? (טיפ מספר 3), אבל הוא פותר את הבעיה באופן חלקי. נוכל להשתמש בו רק בשביל לבדוק האם התוצאה האחרונה היא null ובמידה וכן, לתת ערך דיפולטי אחר.

נראה השבוע גישה מעניינת לפתרון בעיה זו.


אז איך אפשר לפתור את הבעיה?

מהכותרת אפשר לנחש שExtension Methods יכולים להתאים כאן.

למה בעצם הם יכולים להתאים כאן?

קריאה לExtension Method היא קריאה לפונקציה סטטית. בניגוד לInstance methods, קריאה של פונקציה סטטית על null לא בהכרח זורקת Exception!

בד"כ אנחנו בודקים האם הExtension Method נקרא על null, ובמידה וכן זורקים Exception, אך אין זה חייב להיות כך.

כיצד נוכל לנצל יתרון זה? נוכל לכתוב Extension Method כזה:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static TResult With<TSource, TResult>
(this TSource source,
Func<TSource, TResult> accessor)
where TSource : class
where TResult : class
{
if (source == null)
{
return null;
}
else
{
return accessor(source);
}
}

זוהי מתודה שמקבלת אובייקט וDelegate שמקבל את האובייקט ומחזיר אובייקט אחר. מה שהיא עושה זה מחזירה null במידה והאובייקט הוא null, ואחרת מבצעת את הDelegate.

נוכל להשתמש בה בצורה הבאה:

במקום לכתוב את קטע הקוד שכתבנו מעלה, נוכל לכתוב משהו כזה:

1
2
3
ZipCode zipCode = company.With(x => x.Chairman)
.With(x => x.Address)
.With(x => x.ZipCode);

זה מגניב ועוזר לקריאות.

עם זאת זה לא מושלם. מה שהיינו מצפים, זה שיוסיפו לנו אופרטור דומה ל?? המאפשר לנו לעשות את זה בצורה שקופה, משהו כזה:

1
ZipCode zipCode = company.?Chairman.?Address.?ZipCode;

אולי בגרסאות הבאות של C#, יעשו לנו טובה ויוסיפו את זה. בינתיים נאלץ להסתפק בדרכים אחרות.

ראו גם Maybe monad

הערה: אכן בC# 6.0 נוסף Syntax כנ"ל.

שבוע אולי טוב

שתף