221. virtual methods

בC# כמו בשפות תכנות רבות וטובות, קיים משהו שנקרא single dynamic dispatch.

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

מה זה אומר?

כאשר הקוד שלנו מתקמפל, הקומפיילר צריך לדעת לאן לנתב את הקריאות לפונקציות שלנו.

יש פונקציות שכבר בזמן קימפול, הקומפיילר יודע את הכתובת שאליה הוא צריך לגשת כדי לקרוא להן, למשל הפונקציה הבאה:

1
2
3
4
5
6
7
public class MyClass
{
public int Add(int x, int y)
{
return (x + y);
}
}

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

לעומת זאת, בקריאה למתודה וירטואלית, הקומפיילר לא יודע לאן הוא אמור להפנות אותנו:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Shape
{
public virtual void Draw()
{
// ...
}
}
public class Ellipse : Shape
{
public override void Draw()
{
Console.WriteLine("A very cool ellipse");
}
}
public class Circle : Ellipse
{
public override void Draw()
{
Console.WriteLine("A blue circle");
}
}

כעת כשיש לנו קוד כזה:

1
2
Shape shape = GetRandomShape();
shape.Draw();

הקומפיילר לא יודע לאיזו פונקציה הוא אמור להפנות אותנו.

למה? הקריאה לDraw מתבצעת עפ"י הטיפוס של shape בזמן ריצה.

אם הטיפוס הקונקרטי שלנו הוא מסוג Ellipse, ייכתב למסך "A very cool ellipse", ואילו אם הוא Circle, ייכתב למסך "A blue circle".

כעת shape יכול להיות כל דבר, והקומפיילר לא יודע לאיזו צורה הReference שלנו מצביע.

אז מה הוא עושה? הוא מחליט בזמן ריצה לאן להפנות אותנו.

איך זה עובד? מוחזק בצד מבנה נתונים בשם "Virtual table" המנתב טיפוס למתודה שהוא אמור להפעיל. כעת בקריאה לפונקציה בזמן ריצה, ניגש למבנה זה ובודקים איזו מתודה יש להפעיל. (ראו טיפים 319-321)


אז למה זה טוב?

מי שלמד OOP בוודאי יודע שפונקציות וירטואליות משחקות תפקיד חשוב בפולימורפיזם. ("פולימורפיזם זה הswitch-case של OOP")

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

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

עצם העובדה שאנחנו הופכים פונקציה לוירטואלית, מאפשרת לכל אחד לכתוב מה שהוא רוצה שם, כך שקריאה לפונקציה וירטואלית מסכנת אותנו, בכך שאנחנו לא יודעים מה הולך לקרות בה.

לכן חשוב מאוד לבחון איזה פונקציות הופכים לוירטואליות ואילו לא. (בC# פונקציות הן לא וירטואליות דיפולטית, לעומת Java בה הן כן)

בנוסף, כדאי לשים לב שקריאה לפונקציה וירטואלית היא יותר איטית מקריאה לפונקציה רגילה, מאחר ומתבצעת גישה לVirtual table.

שבוע וירטואלי טוב

שתף