177. Static generic classes

הכרנו בעבר (טיפים מספר 26-34) מחלקות ומתודות גנריות.

ראינו ששימוש בטיפוס עם פרמטר גנרי, יוצר מחלקה בזמן ריצה לכל פרמטר גנרי.

מה שלא כל כך מובן מאליו הוא היכולת הבאה:

נניח שיש לנו מחלקה גנרית סטטית:

1
2
3
4
5
6
7
8
9
10
11
12
public static class MyStaticClass<T>
{
private static object mMyObject = new object();
public static object MyObject
{
get
{
return mMyObject;
}
}
}

מה תחזיר השורה הבאה?

1
2
3
4
if (MyStaticClass<string>.MyObject == MyStaticClass<int>.MyObject)
{
// false
}

התנאי יחזיר שקר. הסיבה היא שעבור כל T נוצרת מחלקה (סטטית) אחרת בזמן ריצה ולכן יש לה Member משלה.

נוכל לנצל את היכולת הזו לכל מיני דברים יפים:

למשל, נוכל לכתוב מחלקה כזו:

1
2
3
4
5
6
7
8
9
10
11
12
public static class EmptyArray<T>
{
private static readonly T[] mInstance = new T[] {};
public static T[] Instance
{
get
{
return mInstance;
}
}
}

מחלקה זו מאפשרת לנו להגדיר מערך יחיד ולמחזר אותו, למשל:

1
2
3
4
5
6
7
8
int[] emptyIntArray = EmptyArray<int>.Instance;
string[] emptyStringArray = EmptyArray<string>.Instance;
string[] anotherEmptyStringArray = EmptyArray<string>.Instance;
if (emptyStringArray == anotherEmptyStringArray)
{
// true
}

דוגמה נוספת היא Factory סטטי – נניח שאנחנו רוצים לכתוב איזשהו Factory לאובייקטים.

ראינו מספר דרכים לעשות זאת, ביניהם טיפים מספר 50, 139, 170.

נוכל לנצל שיטה זו כדי ליצור Factory פר טיפוס שמזכיר את טיפ מספר 50:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static class Factory<T>
{
private static Func<T> mBuilder;
public static void RegisterBuilder(Func<T> builder)
{
if (mBuilder != null)
{
throw new ArgumentException("Builder was already initialized.",
"builder");
}
mBuilder = builder;
}
public static T Build()
{
if (mBuilder == null)
{
throw new ArgumentException("Builder wasn't initialized.");
}
return mBuilder();
}
}

ונוכל להשתמש בו כך:

1
2
Factory<Shape>.RegisterBuilder(() => new Triangle());
Shape shape = Factory<Shape>.Build(); // Triangle

זה מאוד מזכיר את טיפ מספר 50, אבל יש הבדל קטן. שם אנחנו שומרים את הDelegateים (הBuilderים) בDictionary ולכן בכל גישה אליו מתבצע חיפוש.

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

שימו לב שהFactory הזה ממפה טיפוס לDelegate אחד, בניגוד לFactoryים שראינו בעבר שממפים מחרוזות לDelegateים מתאימים. (כמובן, אפשר לטפל בזה עם לא הרבה עבודה)


הטריק הזה שימושי עבור חישובים שהם כבדים, אבל הם סטטיים פר טיפוס. למשל, התעסקות בReflection – נניח ואנחנו רוצים למצוא את כל הטיפוסים שמממשים ממשק מסוים או את כל המתודות שיש מעליהן Attribute מסוג מסוים. חישובים כאלה יכולים להתבצע פעם אחת (בעליית האפליקציה, או בפעם הראשונה שהחישוב מתבצע), ובד"כ אין טעם שיתבצעו יותר מפעם אחת.

המשך ערב סטטי גנרי טוב

שתף