-
יש לי מחלקה B שיורשת מ-A
class A { public void printA() { Console.WriteLine("A"); } } class B:A { public void printB() { Console.WriteLine("B"); } }
הקוד ב-Main נראה כך
A a1 = new A(); B b1 = new B(); a1 = b1;
מה שראיתי הוא שאין משמעות לשורה האחרונה, כלומר ה-a1 נשאר a1, ולא הופך להיות הפניה ל-b1.
השאלה היא למה בכלל יש אפשרות כזו, והאם יש אולי עוד משמעות נסתרת לפקודה הזו, שלא עמדתי עליה?תודה רבה C# JAVA
-
@yyy אמר בהפניות לאובייקטים ממחלקות יורשות:
מה שראיתי הוא שאין משמעות לשורה האחרונה, כלומר ה-a1 נשאר a1, ולא הופך להיות הפניה ל-b1.
זה כן הופך להיות הפניה ל-b1. אבל הטייפ שלו לא משתנה.
תסתכל בדוגמה זו:class A { public string name; public void printA() { Console.WriteLine($"Hello, {name} -- from printA"); } } class B : A { public void printB() { Console.WriteLine($"Hello, {name} -- from printB"); } }
A a1 = new A() { name = "yossi" }; B b1 = new B() { name = "yyy" }; a1.printA(); // Hello, yossi -- from printA a1.printB(); // שגיאה בזמן הידור!!! - 'A' does not contain a definition for 'printB' ((B)a1).printB(); // שגיאה בזמן ריצה!!! - Unable to cast object of type 'A' to type 'B'. a1 = b1; a1.printA(); // Hello, yyy -- from printA a1.printB(); // שגיאה בזמן הידור!!! ((B)a1).printB(); // Hello, yyy -- from type B
-
@yossiz אמר בהפניות לאובייקטים ממחלקות יורשות:
@yyy אמר בהפניות לאובייקטים ממחלקות יורשות:
מה שראיתי הוא שאין משמעות לשורה האחרונה, כלומר ה-a1 נשאר a1, ולא הופך להיות הפניה ל-b1.
זה כן הופך להיות הפניה ל-b1. אבל הטייפ שלו לא משתנה.
תסתכל בדוגמה זו:class A { public string name; public void printA() { Console.WriteLine($"Hello, {name} -- from printA"); } } class B : A { public void printB() { Console.WriteLine($"Hello, {name} -- from printB"); } }
A a1 = new A() { name = "yossi" }; B b1 = new B() { name = "yyy" }; a1.printA(); // Hello, yossi -- from printA a1.printB(); // שגיאה בזמן הידור!!! - 'A' does not contain a definition for 'printB' ((B)a1).printB(); // שגיאה בזמן ריצה!!! - Unable to cast object of type 'A' to type 'B'. a1 = b1; a1.printA(); // Hello, yyy -- from printA a1.printB(); // שגיאה בזמן הידור!!! ((B)a1).printB(); // Hello, yyy -- from type B
כלומר זה נהיה שווה ערך לפקודה
A a1 = new B() ?
-
@yyy לא בדיוק שווה ערך כי זה מצביע על ה-B הישן ולא על B חדש.
לבאר יותר את הדברים: צריך להבין את ההבדל בין הסוג של המשתנה לבין הסוג של האובייקט. יכול להיות מצב שיש לך משתנה מסוג A אבל הסוג של האובייקט הוא B.
מתי זה אפשרי?
זה יכול לקרות רק במקרה ש-A ו-B הם סוגים "מתאימים אחד לשני" - כלומר ש-B הוא יורש של A.
כאשר אתה מצהיר על משתנה מסוג A, מותר לו לקבל ערכים שהם מסוג A או מכל סוג אחר שיורש מ-A.
אם תחשוב על זה, תמצא שזה הגיוני. כי למה הצהרת מראש על הסוג של המשתנה? ומה איכפת לו למהדר מה הסוג של המשתנה? התשובה היא שהמהדר לא יכול לדעת איך לעשות פעולת X על משתנה Y בלי לדעת את הסוג שלו.
אבל אם תצהיר על המשתנה שהוא מסוג A, גם אם תתן שם אובייקט מסוג B, המהדר ידע איך לעשות איתו על הפעולות ש-A תומך בהם. כי הם קיימים גם בהגדרה של B.
לכן מותר לך לשים שם אובייקט מסוג B או כל סוג אחר שיורש מ-A. אבל רק בתנאי שתעשה איתו רק פעולות שמוצהרות ב-A.
ברגע שאתה עושה פעולות שמוצהרות רק ב-B אתה בבעיה. כי המהדר כבר לא יודע איך לעשות אותם כי הצהרת על המשתנה שהוא מסוג A.זה פחות או יותר ההסבר. הפשטתי את הדברים קצת לסבר את האוזן.
-
@yossiz אמר בהפניות לאובייקטים ממחלקות יורשות:
זה פחות או יותר ההסבר. הפשטתי את הדברים קצת לסבר את האוזן.
אני רוצה עוד יותר להפשיט ברשותך:
class Animal { public string Sound {get; set;} = 'Unknown'; public void MakeSound() { Console.WriteLine(Sound); } } class Cow : Animal { public void GiveMilk() { Console.WriteLine('Milk!'); } } Animal a; a.Sound = 'Grrrrr!!!!'; Cow cow; cow.Sound = 'Mooo!!!' a.MakeSound(); // prints "Grrrr!!!" cow.MakeSound(); // prints "Mooo!!!" cow.GiveMilk(); // prints "Milk!" ((Cow) a).GiveMilk(); // casting error a = cow; a.MakeSound(); //prints "Mooo!!" a.GiveMilk(); //Error ((Cow) a).GiveMilk(); //Ok //אבל a= new Animal(); a.MakeSound(); //prints Unknown
-