28. Calling generic methods implicitly

בהמשך לשבועי הגנרי הטוב,

אתמול ראינו שאפשר לקרוא לפונקציה גנרית כך:

1
2
3
4
5
6
7
public static T[] Reverse<T>(T[] array)
{
// ...
}
string[] fruits = { "Apple", "Banana", "Orange", "Mango" };
string[] reversed = Reverse<string>(fruits);

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

1
2
string[] fruits = { "Apple", "Banana", "Orange", "Mango" };
string[] reversed = Reverse(fruits);

הקומפיילר החכם יודע לבד מה הType שאנחנו רוצים לקרוא איתו לGeneric Method לפי הארגומנט שאנחנו שולחים לפונקציה.

גם את הדוגמה השנייה של אתמול:

נוכל במקום לכתוב כך:

1
2
3
4
5
6
public static int Count<T>(IEnumerable<T> enumerable)
{
// ...
}
int count = Count<string>(fruits);

לכתוב כך:

1
int count = Count(fruits);

ואפילו אפשר!

1
int length = Count("This string's length is 26"); // length = 26

מה שיפה פה זה שאנחנו קוראים לפונקציה שיכולה לקבל כל IEnumerable<T>, ואנחנו לא צריכים לציין שום דבר על הT שלנו.

אמרתי שבהרבה מאוד מקרים אפשר לעשות את זה. מתי אי אפשר?

כשהקומפיילר לא מבין לבד מה אנחנו רוצים לשלוח לו:

1
public class DumbEnumerable : IEnumerable<int>, IEnumerable<Person>

אם ננסה לקרוא עכשיו לCount נקבל עכשיו את השגיאה הבאה בקימפול:

1
int count = Count(new DumbEnumerable());

The type arguments for method ‘Count(System.Collections.Generic.IEnumerable)’ cannot be inferred from the usage. Try specifying the type arguments explicitly.

דוגמה נוספת היא כשאנחנו מנסים לשלוח אובייקט שהוא לא מType מתאים, למשל:

1
int count = Count(123); // doesn't compile

נשים לב שלפעמים דווקא כן נרצה לציין את הסוג מפורשות.

למשל, בדוגמה הראשונה, ייתכן ולפעמים נרצה לקבל object[] לפעמים מהפונקציה (למשל אם עובדים בתשתית).

אז לא תהיה לנו ברירה, אלא לכתוב מפורשות כך:

1
object[] reversed = Reverse<object>(fruits);

תהנו

שתף