129. Lazy

מדי פעם אנחנו יוצרים Properties שהם Lazy – כלומר, הם לא מאותחלים עד הגישה הראשונה שלהם.

קיימות שתי דרכים נפוצות לעשות זאת:

עבור Reference types: השוואה לnull:

1
2
3
4
5
6
7
8
9
10
11
12
13
public MyClass HeavyProperty
{
get
{
if (m_HeavyProperty == null)
{
// Init m_HeavyProperty
m_HeavyProperty = InitHeavyProperty();
}
return m_HeavyProperty;
}
}

דרך שנייה להשגת מטרה זו היא באמצעות בוליאני המעיד האם הProperty כבר אותחל:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public MyClass HeavyProperty
{
get
{
if (!m_HeavyPropertyInitialized)
{
// Init m_HeavyProperty
m_HeavyProperty = InitHeavyProperty();
m_HeavyPropertyInitialized = true;
}
return m_HeavyProperty;
}
}

כך אנחנו מאתחלים את הProperty רק בגישה הראשונה אליו.

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

בFramework 4.0 הוסיפו לנו טיפוס שמסייע לנו בפתרון בעיה זו, והוא Lazy<T>. איך זה עובד?

זהו טיפוס שמקבל delegate, שבאמצעותו אנחנו נאתחל אובייקט. בגישה הראשונה לProperty ששמו Value יתבצע הdelegate והאובייקט המאותחל ישמר. בשאר הגישות, נפנה לאובייקט שכבר אותחל.

כך נוכל להחליף את המימוש של הProperty בדרך הבאה: ניצור member בשם m_HeavyProperty מסוג Lazy<MyClass>. בConstructor נאתחל אותו:

1
m_HeavyProperty = new Lazy<MyClass>( () => InitHeavyProperty() );

בגישה לProperty פשוט ניגש לValue שלו:

1
2
3
4
5
6
7
public MyClass HeavyProperty
{
get
{
return m_HeavyProperty.Value;
}
}

וזהו!

שימו לב שהConstructor של Lazy<T> מקבל delegate, כך שנוכל להכניס lambda expression נחמד לפעמים לאתחול הProperty ולא סתם קריאה לפונקציה.

(הערה: יתרון נוסף של שימוש בLazy הוא התמיכה של המחלקה בנעילות המונעות אתחול של Member במספר Threadים במקביל. ראו גם טיפ מספר 283)

המשך יום טוב ועצלן

שתף