36. Array covariance

היי,

במדעי המחשב קיים מושג שנקרא covariance ומושג מקביל שנקרא contravariance.

הסבר פשוט של המושג הוא הבא:

נניח שיש לנו פונקציה $ f $ שפועלת על Typeים (מקבלת Type ומחזירה Type).

נאמר שהיא covariant אם לכל טיפוס $ B $ שיורש מטיפוס $ A $, מתקיים $ f(B) $ יורש מ$ f(A) $.

כתיב:

$ B : A \implies f(B) : f(A) $

לדוגמה,

נניח שיש לנו את הTypeים הבאים

1
2
3
public class Shape
public class Circle : Shape
public class Triangle : Shape

טבעי לצפות שהפונקציה שמעבירה כל טיפוס $ T \mapsto \text{List}[T] $ תהיה covariant, שהרי היינו מצפים ש List<Circle> ירש מList<Shape>שהרי Circle יורש מShape.

אבל דבר זה גורר סתירה, כי אז נוכל לכתוב את השורות הבאות:

1
2
List<Shape> shapes = new List<Circle>();
shapes.Add(new Triangle());

מה הבעיה בשורות אלו? הכנסנו לרשימה של מעגלים, משולש!

מאחר ולא רצו שנחטוף exception בזמן ריצה, החליטו שList<T> לא יהיה covariant (ולא רק הוא), לכן השורה הראשונה של ההשמה, בכלל לא מתקמפלת.

הסיבה לכך היא שאחד הדברים שgenerics מעניקים לנו זה type safety, ומונעים מאיתנו לעשות שטויות כאלה כבר בזמן קימפול.

עם זאת, covariance כן ממומש בC#, ואפילו בC# 1.0:

אם ניקח את הדוגמה הקודמת ונשנה אותה קצת:

1
Shape[] shapes = new Circle[1];

השורה הזאת מתקמפלת. כלומר מתקיים ש $ T \mapsto T[] $ הוא covariant.

במילים אחרות

1
Circle[] : Shape[]

מה יקרה אם ננסה לעשות משהו כמו קודם?

1
2
Shape[] shapes = new Circle[1];
shapes[0] = new Triangle();

נקבל exception בזמן ריצה

"Attempted to access an element as a type incompatible with the array."

המסקנה היא שצריך מאוד להיזהר עם covariance של מערכים בC#.

הערה: Covariance של מערכים בC# עובד רק על Reference Types: למשל לא מתקיים

1
int[] : object[]

שבוע מעולה

שתף