C# 4.0 Part 12 - Covariance and Contravariance
המושג הזה הוא קצת וירטואלי למי שלא מכיר אותו, אז כדאי שנעבור על המושג לפני שנראה מה התחדש ב - C# 4.0.
Covariance אומר את הדבר הבא: אפשר להגדיר delegate שמחזיר אובייקט ולתת מתודה שבפועל מחזירה מישהו שיורש ממנו, כלומר:
delegate object CovarianceDelegate();
static void Main()
{
CovarianceDelegate cd = CovarianceMethod;
}
static Person CovarianceMethod()
{
}
וזה הגיוני כי זה עומד בחוקי ה - Object Oriented (שאפשר לשלוח בן לאבא).
Contravariance אומר את הדבר הבא: אפשר להגדיר delegate שמקבל אובייקט ולתת מתודה שבפועל מקבלת את האבא, כלומר:
delegate void ContravarianceDelegate(Person obj);
static void Main()
{
ContravarianceDelegate cva = ContravarianceMethod;
}
static void ContravarianceMethod(object obj)
{
}
וכמו שאמרתי זה היה אחד מהחידושים ב - C# 2.0, היום ב - (C# 4.0) הרחיבו את המושג Covariance and Contravariance גם עבור Generic Interface ו - Generic Delegate. (מומלץ לקרוא את ההסבר ב -
MSDN).
למעשה Generic Interface או Generic Delegate נקראים בשם Variant אם ה - Generic Parameter (כלומר ה - T) מוגדר כ - Covariance או כ - Contravariance.
דוגמא ל - Generic Interface שה - T שלו מוגדר כ - Covariance הוא IEnumerable. הקוד הבא חוקי רק ב - C# 4.0
List<string> strings = new List<string>();
IEnumerable<object> objects = strings;
למעשה ה - T של IEnumerable מוגדר שהוא יכול להחזיר כל מי שיורש מ - T ולכן השורה השנייה הינה חוקית, שימו לב איך הוגדר ה - Interface.
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
ה - out ל - T מגדיר שכל מה שיורש מ - T יכול לחזור בפונקציה GetEnumerator (וזה לא פשוט, מכיון ש - IEnumerable של string לא יורש מ - IEnumerable של object - אבל ה - out מגדיר שמסתכלים על ה - T והיות ש - string יורש מ - object אפשר לעשות את זה).
דומגא ל Interface שהוגדר כ - Contravariance הוא ICompare. הקוד הבא הינו חוקי רק ב - C# 4.0
public class Class1
{
static void Main()
{
IComparer<Employee> abc = new Person();
}
}
class Employee : Person
{
}
class Person : IComparer<Person>
{
}
זאת אומרת שאני יכול לשלוח לאובייקט מסוג IComparer של Employee אובייקט שמממש את IComparer של Person היות ש - Person הוא האבא של Employee.
(זה הגיוני, כי אם Employee יורש מ - Person, אז ברור שמתודה שיודעת להשוות בין שני Person תדע גם להשוות בין שני Employee)
ה - Interface מוגדר כך:
public interface IComparer<in T>
{
int Compare(T x, T y);
}
ברגע שה - T מוגדר כ - in אפשר לשלוח פנימה כל מי ש - T יורש ממנו.
ב - MSDN יש כמה דוגמאות שימושיות לזה.
כאן וכאן והסבר מקיף נרחב נמצא
כאן