בתת פרק זה נחזק את הבנת פולימורפיזם באמצעות פתרון שאלה מבגרות 2025
מדעי המחשב, קיץ תשפ”ה, מס’ 899271 - תרגיל בע”מ 24
// מחלקות נתונות
public class A {
protected int i;
public A(int i) { this.i = i; }
}
public class B : A {
public B(int i) : base(i + 1) { }
public bool Foo(Object myObject) {
Console.WriteLine("Foo1");
return ((myObject is B) && (this.i == ((B)myObject).i));
}
public virtual bool Foo(B myB, int num) {
Console.WriteLine("Foo2");
return (this.i + myB.i) < num;
}
}
public class C : B {
public C(int i) : base(i) { }
public bool Foo(int num) {
Console.WriteLine("Foo3");
return (this.i != num);
}
}
public class D : B {
public D(int i) : base(i * 2) { }
public override bool Foo(B myB, int num) {
Console.WriteLine("Foo4");
return base.Foo(myB, 20);
}
}
א. סרטטו את תרשים ההיררכיה של המחלקות A, B, C, D (חץ מייצג הורשה).
לחצו לפתרון
לחצו לתרשים UML (לא נדרש)
ב. נתונה המחלקה Tester
public class Tester {
public static void Main(string[] args) {
A a = new A(1);
B b = new B(2);
C c = new C(3);
D d = new D(4);
B bd = new D(5);
A ac = new C(6);
//***
}
}
ב1. סרטטו את העצמים שנוצרו בפעולה Main.
ב2. קטע הקוד שנוסף ל-Main
Console.WriteLine(c.Foo(5)); //(1)
Console.WriteLine(d.Foo(a)); //(2)
Console.WriteLine(bd.Foo(b)); //(3)
Console.WriteLine(bd.Foo(b, 1)); //(4)
Console.WriteLine(((C)ac).Foo(c));//(5)
ב2. כתבו את הפלט של הפעולות (ציינו ליד כל פלט את מספר השורה).
עץ החלטה (אלגוריתם לשימוש בבחינה)
כדי לפתור שאלות כאלה:
- בִּדְקוּ איזה סוג מוצהר למצביע.
- חַפְּשׂוּ מתודות מתאימות לפי חתימה.
- אם יש
virtual/override→ נקבע לפי סוג האובייקט בפועל. - אם אין → נקבע לפי הסוג המוצהר.
- תמיד חִשְׁבוּ על ערכי
iשנקבעו בבנאים!
ניתוח השאלה האחרונה – פולימורפיזם וקריאות לפעולות
רקע קצר
ב-C#, כאשר יש לנו הורשה בין מחלקות ושמות פעולות זהים, חשוב להבחין בין:
- Overloading (העמסת פעולות) – פעולות עם אותו שם אך חתימות שונות.
- Overriding (הסתרה/דריסה) – פעולה במחלקת הבת שמחליפה התנהגות של פעולה וירטואלית מהאב.
בנוסף, ישנו שלב בחירה בזמן קומפילציה (איזו גרסה מתאימה לפי סוג המשתנים המוצהרים) ושלב הרצה בזמן אמת (כאשר קיימת פעולה virtual או override, נקבעת לפי סוג האובייקט בפועל).
העצמים שנוצרים
A a = new A(1); // אובייקט מטיפוס A
B b = new B(2); // אובייקט מטיפוס B
C c = new C(3); // אובייקט מטיפוס C
D d = new D(4); // אובייקט מטיפוס D
B bd = new D(5); // מצביע B אל אובייקט מטיפוס D
A ac = new C(6); // מצביע A אל אובייקט מטיפוס C
ניתוח הקריאות
(1) c.Foo(5)
- סוג מוצהר:
C - יש פעולה מתאימה:
Foo(int)ב-C. - מודפסת ההודעה “Foo3”.
-
הערך:
(this.i != num)=(3+1 != 5)→ כיון ש־C יורשת מ-B עםi+1. נבדוק:new C(3)קורא ל-base(3)= B(3) → ואז B מוסיף+1→ i=4.- בדיקה:
4 != 5→ true.
פלט: Foo3 ואז True.
(2) d.Foo(a)
- סוג מוצהר:
D. - פעולה עם חתימה
(Object)קיימת רק ב-B (הורשה). - נבחרת
Foo(Object)(לא וירטואלית). - מודפסת ההודעה “Foo1”.
- בדיקה:
aאיננוB, לכן תנאיmyObject is Bנכשל. - מחזיר false.
פלט: Foo1 ואז False.
(3) bd.Foo(b)
- סוג מוצהר:
B. -
חיפוש מתודות:
- יש
Foo(Object)ו-Foo(B, int).
- יש
- החתימה
(B)אינה קיימת ישירות. - הקומפיילר יבחר ב-
Foo(Object)(כיbהואBויכול להיחשב כ-Object). - מודפסת “Foo1”.
-
בדיקה:
this.i == ((B)b).i? עבור bd = new D(5).new D(5)→ base(5*2=10) = B(10+1) → i=11.b = new B(2)→ i=3.- 11==3? → false.
פלט: Foo1 ואז False.
(4) bd.Foo(b, 1)
- סוג מוצהר:
B. - החתימה
(B, int)קיימת והיא virtual. - בזמן ריצה: bd מצביע על אובייקט מטיפוס D, לכן תופעל הפעולה Foo(B,int) של D.
- הדפסה: “Foo4”.
- הפונקציה קוראת ל-
base.Foo(myB, 20). - זה בעצם B.Foo עם num=20.
this.i=11,myB.i=3.- 11+3 < 20? כן → 14 < 20 → true.
פלט: Foo4, Foo2, ואז True.
(5) ((C)ac).Foo(c)
acמצביע A על אובייקט מטיפוס C.- נעשית המרה ל-C.
- ניסיון קריאה:
Foo(C)לא קיים. -
קיימות מתודות:
- Foo(int)
- Foo(Object)
- Foo(B,int)
- המתאימה היא Foo(Object).
- לכן יופעל
Foo(Object)של B. - הדפסה: “Foo1”.
- השוואה:
this.i == ((B)c).i. acנבנה כ-new C(6)→ i = (6+1)=7.c = new C(3)→ i=4.- 7==4? → false.
פלט: Foo1 ואז False.
סיכום הפלטים
Foo3,TrueFoo1,FalseFoo1,FalseFoo4,Foo2,TrueFoo1,False
בגרות 2021 נבצרים, בקישור זה, מכילה שאלה ברמת קושי דומה
מתי הקביעה היא בזמן קומפילציה ומתי זה דינאמי?
למה כשאני מגדיר ToString בלי המילה override, אני עדיין יכול לכתוב Console.WriteLine(c.ToString())