140. MakeGenericType method

בהמשך לשבוע הטיפוסי הטוב,

הכרנו קצת את Type וראינו איך אפשר ליצור instance חדש מType נתון.

נניח שיש לנו כמו אתמול Type שהשגנו אותו מאיזשהו מקום (למשל, מתוך קונפיגורציה).

איך נוכל ליצור List של טיפוסים כאלה?

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

1
2
3
4
5
public IList CreateList(Type givenType)
{
List<givenType> list = new List<givenType>();
return list;
}

אלא שקוד זה לא יתקמפל:

The type or namespace name ‘givenType’ could not be found (are you missing a using directive or an assembly reference?)

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

כדי לפתור בעיה זו, נוכל להשתמש בפונקציה MakeGenericType של Type, המקבלת Type של טיפוס גנרי Unbound, ומזריקה אליו את הפרמטרים הגנריים שלו.

למשל:

1
2
3
4
Type unboundList = typeof (List<>);
Type stringList =
unboundList.MakeGenericType(typeof (string)); // typeof(List<string>)

או

1
2
3
4
Type unboundDictionary = typeof (Dictionary<,>);
Type stringList =
unboundDictionary.MakeGenericType(typeof (string), typeof(int)); // typeof(Dictionary<string, int>)

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

1
2
3
4
5
6
7
8
9
10
11
public IList CreateList(Type givenType)
{
Type unboundListType = typeof (List<>);
Type givenTypeList =
unboundListType.MakeGenericType(givenType);
IList list = (IList)Activator.CreateInstance(givenTypeList);
return list;
}

קצת טרמינולוגיה:

במחלקה

1
2
3
public class List<T>
{
}

הטיפוס List<T> נקרא open generic type (כיוון שT יכול להיות כל דבר)

כאשר הטיפוס הגנרי מצוין, למשל List<int>, טיפוס זה נקרא closed generic type.

כאשר אנחנו יוצרים טיפוס כזה

1
Type unboundDictionary = typeof (Dictionary<,>);

הטיפוס נקרא Unbound generic type.

בזמן ריצה, לא קיימים open generic types.

לא ניתן ליצור instanceים של unbound generic typeים:

1
2
3
4
Type unboundDictionary = typeof (Dictionary<,>);
object dictionary =
Activator.CreateInstance(unboundDictionary);

נקבל Exception כזה:

Cannot create an instance of System.Collections.Generic.Dictionary`2[TKey,TValue] because Type.ContainsGenericParameters is true.

סופ"ש טיפוסי טוב

שתף