38. Method group contravariance

בהמשך לטיפ היומי של אתמול, נניח שיש לנו delegate כזה

1
public delegate void CircleDrawer(Circle circle);

אבל אנחנו הגדלנו ראש, והצלחנו לממש פונקציית ציור של כל צורה:

1
2
3
4
public static void DrawShape(Shape shape)
{
// ...
}

היינו רוצים שנוכל להשתמש בפונקציה של ציור צורה כללית DrawShape, כדי לצייר בפרט מעגל.

שוב, מצד אחד זה נראה כאילו אנחנו צריכים להחליף את החתימה של הפונקציה, כדי שתקבל Shape, או לחלופין ליצור פונקציה שקוראת לה עם החתימה של CircleDrawer, שכל ההבדל היא שהפונקציה מקבלת Circle במקום Shape.

גם כאן, בC# 2.0 חשבו על זה, ונתנו לנו תמיכה בהסבות כאלה ברמת הקוד:

1
2
CircleDrawer circleDrawer =
new CircleDrawer(DrawShape); // Compiles!

קצת על contravariance – אם אמרנו שהמשמעות של covariance היא כזאת:

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

כתיב:

$ B : A \implies f(B) : f(A)$
אז המשמעות של Contravariance היא אנלוגית:

פונקציה f של Typeים היא contravariant היא אם לכל טיפוס $ B $ שיורש מטיפוס $ A $ , מתקיים $ f(A) $ יורש מ$ f(B) $:

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

שימו לב להבדל הדק, שהיחס בין הירושה לא נשמר, אלא מתהפך.

היינו מצפים, למשל, בעקבות מה שרשמתי למעלה, שאם יש לנו את שני הdelegateים הבאים:

1
2
public delegate void ShapeDrawer(Shape shape);
public delegate void CircleDrawer(Circle circle);

יתקיים משהו בסגנון

1
ShapeDrawer : CircleDrawer

כי ראינו שאפשר להכניס לתוך CircleDrawer מתודה עם חתימה של ShapeDrawer.

בC# 2.0 אין תמיכה בזה, ולמשל הקוד הבא מתקמפל:

(הפונקציה היא כמקודם)

1
2
3
4
5
6
7
8
9
public static void DrawShape(Shape shape)
ShapeDrawer shapeDrawer =
new ShapeDrawer(DrawShape); // Compiles
CircleDrawer circleDrawer =
new CircleDrawer(DrawShape); // Compiles!
circleDrawer = shapeDrawer; // Doesn't compile

יום קונטרה וואריאנטי מצוין

שתף