137. GetType

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

ראינו שאנחנו מסוגלים לקבל Type באמצעות הKeyword ששמו typeof.

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

למשל:

1
2
3
4
5
6
7
8
IEnumerable<string> myStrings = new List<string>();
Type enumerableType = typeof (IEnumerable<string>);
Type instanceType = myStrings.GetType(); // typeof(List<string>)
if (instanceType == enumerableType)
{
// False
}

בשונה מהKeyword ששמו typeof, הפונקציה GetType נקראת בזמן ריצה וכך הטיפוס מפוענח בזמן ריצה לפי הטיפוס של הinstance.

קיימת עוד פונקציה ששמה GetType המאפשרת לנו לקבל Type לפי הFullName שלו (זוכרים מאתמול?):

1
2
3
4
5
6
Type myType = Type.GetType("System.Int32");
if (myType == typeof(int))
{
// True
}

היא מחפשת את הType שאנחנו מחפשים מהAssemblyים (הDLLים) הטעונים לנו בזכרון.


אחד השימושים הלא נכונים שראיתי של הפונקציה GetType הוא להשתמש בו בכדי לבדוק מה הסוג שלthis, ולפיו לבצע פעולה, כלומר משהו כזה:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Shape
{
public void Draw()
{
if (this.GetType() == typeof(Shape))
{
// Draw a shape
}
if (this.GetType() == typeof(Triangle))
{
// Draw a triangle
}
else if (this.GetType() == typeof(Circle))
{
// Draw a shape
}
// ...
}
}

הדבר הזה לא נכון, מאחר ומחלקה לא אמורה להכיר את המחלקות בת שלה. (אחד העקרונות של OOP)

הפתרון הנכון הוא להפוך את Draw לפונקציה וירטואלית:

1
2
3
4
5
6
7
public class Shape
{
public virtual void Draw()
{
// Draw a shape
}
}

ולדרוס אותה במחלקות בת:

1
2
3
4
5
6
7
public class Circle : Shape
{
public override void Draw()
{
// Draw a circle
}
}

עוד שימוש לא נכון בGetType הוא להשתמש בו בכדי לוודא שאובייקט הוא מסוג טיפוס ידוע, בשביל לעשות הסבה בטוחה.

כלומר לעשות משהו כזה:

1
2
3
4
5
6
object instance;
if (instance.GetType() == typeof(Shape))
{
Shape shape = (Shape) instance;
}

מה הבעיה בקטע קוד הזה?

יכול להיות שinstance הוא טיפוס שיורש מShape (למשל Circle), ואז לא נכנס לתנאי (כי GetType יחזיר typeof(Circle) ולא typeof(Shape)), ולכן לא תתבצע ההסבה.

הדרך הנכונה היא להשתמש בKeywordים ששמם is או as:

1
2
3
4
if (instance is Shape)
{
Shape shape = (Shape) instance;
}

כזכור, is בודק שמופע של אובייקט הוא מטיפוס נתון או יורש ממנו, ומאחר וCircle יורש מShape, התנאי מתקיים.

לחלופין אפשר להשתמש בas:

1
2
3
4
5
6
Shape shape = instance as Shape;
if (shape != null)
{
// ...
}

ראו גם טיפ מספר 12.

(שימו לב שגם שילוב של שני האנטי-טיפים הוא לא נכון, כלומר לכתוב if (this is Sparta) זו לא כתיבה נכונה, בדומה לGetType)

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

שתף