זהו דף אבחון מהיר. בכל דוגמה קראו קודם את הקוד, אחר כך שאלו: האם הקומפיילר בכלל מרשה להמשיך, ואם כן, האם זמן הריצה יצליח.
שגיאות קומפילציה
1. הפעולה לא קיימת על הטיפוס המוצהר
Object a2 = new Antelope(10);
a2.IsSame(a2);
שגיאת קומפילציה. הטיפוס המוצהר הוא Object, ול-Object אין פעולה בשם IsSame. לכן הקוד נעצר עוד לפני זמן ריצה.
תיקון מינימלי אם ידוע שהעצם בזכרון הוא יונק:
((Mammal)a2).IsSame((Mammal)a2);
ה-cast כאן לא משנה את האובייקט בזכרון. הוא רק מאפשר לקומפיילר לראות את הפעולה דרך טיפוס מתאים יותר.
2. אין overload מתאים לארגומנט הנתון
Antelope a1 = new Antelope(10);
Object a2 = new Antelope(10);
a1.IsSame(a2);
שגיאת קומפילציה. השם IsSame קיים, אבל אין חתימה מתאימה עבור ארגומנט מטיפוס מוצהר Object. זו לא שגיאת זמן ריצה, כי אפילו לא נבחרה פעולה חוקית.
תיקוני cast אפשריים, לפי הפעולה שרוצים להפעיל:
a1.IsSame((Antelope)a2);
a1.IsSame((Mammal)a2);
שני התיקונים אינם שקולים. ה-cast ל-Antelope יכול לבחור overload אחד, וה-cast ל-Mammal יכול לבחור overload או מימוש אחר.
3. גישה לחבר שאינו נגיש
Antelope a1 = new Antelope(10);
Console.WriteLine(a1.weight);
שגיאת קומפילציה. השדה weight הוא protected, ולכן קוד חיצוני שאינו מחלקת בן אינו רשאי לגשת אליו ישירות.
תיקון טיפוסי: להשתמש בפעולה ציבורית מתאימה, למשל:
Console.WriteLine(a1.GetWeight());
כאן cast לא יפתור דבר. הבעיה היא הרשאת גישה, לא טיפוס.
4. נדרשת המרה מפורשת
Object obj = new Beaver(10);
Beaver b = obj;
שגיאת קומפילציה. המשתנה obj מוכרז כ-Object, ולכן נדרשת המרה מפורשת ל-Beaver. בלי cast הקומפיילר לא מאשר את ההשמה.
תיקון מינימלי:
Beaver b = (Beaver)obj;
אחרי שהוספתם cast, השגיאה יכולה לעבור מזמן קומפילציה לזמן ריצה. אם האובייקט בזכרון אינו Beaver, תתקבל InvalidCastException.
שגיאות זמן ריצה
5. cast מפורש חוקי תחבירית, אבל שגוי בפועל
Object a2 = new Antelope(10);
Console.WriteLine(((Beaver)a2).GetWeight());
שגיאת זמן ריצה: InvalidCastException. ההמרה מ-Object ל-Beaver מותרת תחבירית, אבל האובייקט בזכרון הוא Antelope, ולכן ההמרה נכשלת בזמן הרצה.
תיקון פשוט אם רוצים לעבוד עם הטיפוס האמיתי שבזכרון:
Console.WriteLine(((Antelope)a2).GetWeight());
כשאין ודאות לגבי הטיפוס בזכרון, עדיף לבדוק לפני ההמרה:
if (a2 is Beaver beaver)
Console.WriteLine(beaver.GetWeight());
6. הפניה ל-null
Beaver b = null;
Console.WriteLine(b.GetWeight());
שגיאת זמן ריצה: NullReferenceException. המשתנה קיים, אבל הוא לא מצביע על אובייקט. ניסיון לקרוא פעולה דרך null יפיל את התוכנית בזמן הרצה.
תיקון בסיסי:
if (b != null)
Console.WriteLine(b.GetWeight());
7. מערך נראה חוקי, אבל סוג האיברים האמיתי לא מתאים
object[] arr = new string[1];
arr[0] = new object();
שגיאת זמן ריצה: ArrayTypeMismatchException. המשתנה מוכרז כ-object[], אבל המערך שבזכרון הוא למעשה string[]. לכן הכנסת object שאינו string נכשלת בזמן הרצה.
תיקון אפשרי 1: ליישר בין הטיפוס המוצהר לבין הטיפוס האמיתי של המערך.
object[] arr = new object[1];
arr[0] = new object();
תיקון אפשרי 2: אם רוצים מערך מחרוזות, גם האיבר חייב להיות מחרוזת.
string[] arr = new string[1];
arr[0] = "hello";
מקרה מבלבל שאינו שגיאה
8. new גורם להפעלה מפתיעה, אבל לא לשגיאה
class A
{
public void F() { Console.Write("A"); }
}
class B : A
{
public new void F() { Console.Write("B"); }
}
A x = new B();
x.F();
אין כאן שגיאה בכלל. יודפס A, כי F אינה חלק משרשרת override. המתודה של B רק מסתירה את זו של A, והקריאה דרך הטיפוס המוצהר A נשארת עם A.F().
אם רוצים פולימורפיזם אמיתי, צריך virtual ו-override:
class A
{
public virtual void F() { Console.Write("A"); }
}
class B : A
{
public override void F() { Console.Write("B"); }
}
משפט סיכום
אם אין חתימה חוקית, זו שגיאת קומפילציה. אם יש חתימה חוקית אבל האובייקט בפועל לא מתאים, זו שגיאת זמן ריצה. ברוב שאלות הפולימורפיזם, ההבדל הזה הוא כל השאלה.