הכלל הקבוע הוא כזה: קודם בודקים מה הקומפיילר רואה, ורק אחר כך מה האובייקט באמת. במילים אחרות: קודם טיפוס מוצהר, ורק אם צריך אחר כך טיפוס בזכרון.
האלגוריתם
flowchart TD
A[יש קריאה לפעולה] --> B{"יש cast מפורש על המשתנה או על הארגומנט?"}
B -->|כן| B1{"האם ה-cast חוקי תחבירית בין הטיפוסים?"}
B1 -->|לא| E1[שגיאת קומפילציה: המרה לא חוקית]
B1 -->|כן| B2{"האם הטיפוס בזכרון מתאים ל-cast?"}
B2 -->|לא| E2[שגיאת זמן ריצה: InvalidCastException]
B2 -->|כן| C[ממשיכים עם התוצאה של ה-cast]
B -->|לא| C
C --> D[קובעים את הטיפוס המוצהר של המשתנה שמשמאל לנקודה]
D --> E[מחפשים בשם הפעולה רק במחלקה של הטיפוס המוצהר ובאבותיה]
E --> F{"קיימת חתימה מתאימה לארגומנטים?"}
F -->|לא| E3[שגיאת קומפילציה: אין פעולה מתאימה]
F -->|כן| G[נבחר overload אחד בזמן קומפילציה]
G --> H{"הפעולה שנבחרה היא virtual או override?"}
H -->|לא| I[ההפעלה נקבעת לפי הטיפוס המוצהר]
H -->|כן| J[עוברים לטיפוס בזכרון]
J --> K{"קיים override בשרשרת עבור הטיפוס בזכרון?"}
K -->|כן| L[תרוץ הגרסה של הטיפוס בזכרון]
K -->|לא| M[תרוץ הגרסה שנבחרה קודם]
ארבעה כללי זהב
overloadנבחר לפי טיפוס מוצהר של המקבל ושל הארגומנטים.overrideנבחר לפי טיפוס בזכרון רק אחרי שכבר נבחרה חתימה חוקית.newאינוoverride. ברוב השאלות צריך להתייחס אליו כמו להפעלה לפי טיפוס מוצהר.castיכול לפתור שגיאת קומפילציה, אבל גם ליצור שגיאת זמן ריצה אם הטיפוס בזכרון אינו מתאים.
דוגמת בזק 1: new מול override
class A
{
public void F() { Console.Write("A"); }
public virtual void G() { Console.Write("a"); }
}
class B : A
{
public new void F() { Console.Write("B"); }
public override void G() { Console.Write("b"); }
}
A x = new B();
x.F();
x.G();
בקריאה x.F() נשתמש בטיפוס המוצהר A, ולכן יודפס A. בקריאה x.G() כבר קיימת שרשרת virtual/override, ולכן הטיפוס בזכרון B מכריע ויודפס b.
דוגמת בזק 2: overload נכשל לפני זמן ריצה
Antelope a1 = new Antelope(10);
Object a2 = new Antelope(10);
a1.IsSame(a2);
כאן בכלל לא מגיעים לשלב של הטיפוס בזכרון. הקומפיילר מחפש חתימה דרך הטיפוס המוצהר של a2, רואה Object, ולא מוצא overload מתאים.
דוגמת בזק 3: cast משנה את המסלול
Antelope a1 = new Antelope(10);
Object a2 = new Antelope(10);
a1.IsSame((Antelope)a2);
ה-cast משנה את מה שהקומפיילר רואה עבור הארגומנט, ולכן עכשיו אפשר לבחור overload מתאים. אם הטיפוס בזכרון באמת Antelope, ההרצה גם תצליח.
משפט סיום קצר
אם אתם לא יודעים איזו פעולה תרוץ, אל תנסו לנחש. לכו לפי הסדר: cast אם יש, אחר כך טיפוס מוצהר, אחר כך חתימה, ורק בסוף טיפוס בזכרון.