136. typeof keyword

אחד הדברים המגניבים ביותר שאפשר לעשות בnet הוא לכתוב קוד שמסוגל לחלץ מידע על קוד שכתבנו.

כאשר התוכנה שלנו מתקמפלת, חלק מהמידע בה נעלם, כגון שמות של משתנים לוקאליים והערות, אבל רוב המידע נשאר נגיש. (למשל שמות של מחלקות שלנו והMemberים שלהן)

קיימת לנו גישה למידע זה, הנקרא גם Metadata, באמצעות Reflection המאפשר לנו לקבל מחלקות המתארות את המידע בו אנחנו מעוניינים.

נכיר בטיפ זה את Type – טיפוס של הFramework המתאר טיפוסים אחרים, ואת הkeyword ששמו typeof.


כאמור המחלקה Type מתארת טיפוסים netים אחרים. השימוש הפשוט ביותר במחלקה זו הוא באמצעות הkeyword ששמו typeof:

1
Type intType = typeof (int);

ככה קיבלנו instance של Type המתאר את הטיפוס int.

נוכל לעשות כל מיני דברים נחמדים כמו:

1
2
3
Console.WriteLine(intType.Name); // Int32
Console.WriteLine(intType.Namespace); // System
Console.WriteLine(intType.FullName); // System.Int32

או להדפיס, למשל, את כל הממשקים שהוא מממש:

1
2
3
4
5
6
7
8
9
foreach (Type currentInterface in intType.GetInterfaces())
{
Console.WriteLine(currentInterface.FullName);
}
// System.IComparable
// System.IFormattable
// System.IConvertible
// System.IComparable`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
// System.IEquatable`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

את הKeyword ששמו typeof ניתן להפעיל על כל טיפוס שידוע בזמן קימפול, והנה מספר דוגמאות:

1
2
3
4
5
Type interfaceType = typeof (IComparable); // Works on interfaces
Type arrayType = typeof (int[]); // Works on arrays
Type twoDimensionalArrayType = typeof (int[,]); // Even if they have a larger dimension
Type listType = typeof (List<int>); // Even on generic types
Type unboundListType = typeof (List<>); // Also on unbound generic types

הדוגמה האחרונה היא יותר חריגה, מאחר ואם ננסה לכתוב קוד כזה:

1
List<> myList = new List<>();

הוא לא יתקמפל. בJava, קיים פתרון באמצעות Wildcards. בC# אין פתרון לדבר כזה בזמן קימפול, אבל באמצעות Reflection אפשר לעשות דברים דומים לזה בזמן ריצה.


קצת בקשר לFullName: ראינו בדוגמה למעלה שהדפסה של FullName של טיפוס נראית בעצם כמו שרשור של הNamespace שלו עם הName שלו.

הדבר נכון בד"כ, אבל יש שני מקרים בהם הוא לא נכון:

  • עבור טיפוסים מקוננים (Nested Types), למשל:
1
2
3
4
5
6
public class MyType
{
public class MyNestedType
{
}
}

נקבל:

1
2
3
4
Type myNestedType = typeof(MyType.MyNestedType);
Console.WriteLine(myNestedType.Name); // MyNestedType
Console.WriteLine(myNestedType.Namespace); // MyNamespace
Console.WriteLine(myNestedType.FullName); // MyNamespace.MyType+MyNestedType
  • עבור טיפוסים גנריים: נקבל בName את שם המחלקה משורשרת עם ` שאחריו מופיע את מספר הארגומנטים הגנריים:
1
2
3
Type genericType = typeof(Dictionary<int, string>);
Console.WriteLine(genericType.Name); // Dictionary`2
Console.WriteLine(genericType.Namespace); // System.Collections.Generic

אבל בFullName נקבל משהו קצת אחר:

1
2
Console.WriteLine(genericType.FullName);
// System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

שימו לב שמצוינים בסוגריים הטיפוסים הגנריים

  • אם הטיפוס הגנרי היה unbound, כלומר בלי פרמטרים גנריים מצוינים, היינו באמת מקבלים משהו כזה:
1
2
Type unboundType = typeof(Dictionary<,>);
Console.WriteLine(unboundType.FullName); // System.Collections.Generic.Dictionary`2

שיהיה שבוע טיפוסי (עד כמה שאפשר)

שתף