66. extension methods

אחד הדברים החזקים בתכנות מונחה עצמים הוא העובדה שאנחנו יכולים לרשת ממחלקה ולהרחיב אותה, למשל להוסיף עוד memberים עם לוגיקה משלנו.

עם זאת, יש מספיק מחלקות שהן sealed ולא נוכל לרשת מהן, ולכן לא נוכל להרחיב אותן.

למשל, יכול להיות שהיינו רוצים להוסיף למחלקה הידועה string מתודה שנקראת Reverse שהופכת את המחרוזת.

היינו רוצים לכתוב בעצם משהו כזה:

1
2
3
4
5
6
7
8
9
10
11
12
public string Reverse()
{
List<char> reverseChars =
new List<char>(this.Length);
foreach (char currentChar in this)
{
reverseChars.Insert(0, currentChar);
}
return new string(reverseChars.ToArray());
}

אבל אנחנו לא יכולים לשנות את הקוד של המחלקה string.

בנוסף, אנחנו גם לא יכולים לרשת מstring ולהוסיף שם את המתודה.

מה שאנחנו כן יכולים לעשות זה דבר כזה:

שימו לב שהמתודה Reverse לא ניגשת לMemberים פרטיים של string ולכן נוכל ליצור פונקציה סטטית ששקולה לה:

1
2
3
4
5
6
7
8
9
10
11
12
public static string Reverse(string givenString)
{
List<char> reverseChars =
new List<char>(givenString.Length);
foreach (char currentChar in givenString)
{
reverseChars.Insert(0, currentChar);
}
return new string(reverseChars.ToArray());
}

עם זאת קריאה כזאת היא פחות יפה:

1
string reversedString = Reverse("Hello world");

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

1
2
string reversedString =
StringUtilities.Reverse("Hello world");

הסתרבלנו קצת.


פה באו עם אחד הדברים המהפכניים, ולדעתי הדבר הכי מהפכני בC# 3.0, מה שנקרא Extension methods.

הדבר מאפשר לנו לקרוא למתודה סטטית בצורה Postfix במקום Prefix. כלומר לעשות משהו כזה:

1
string reversedString = "Hello world".Reverse();

כדי לעשות את זה צריך לעשות את הדברים הבאים:

צריך להכניס את המתודה הסטטית שלנו למחלקה סטטית, למשל StringUtilities.

אחרי זה צריך להוסיף לפני הType של המשתנה שאנחנו רוצים לקרוא למתודה שלו בצורה Postfix את הkeyword ששמו this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static class StringUtilities
{
public static string Reverse(this string givenString)
{
List<char> reverseChars =
new List<char>(givenString.Length);
foreach (char currentChar in givenString)
{
reverseChars.Insert(0, currentChar);
}
return new string(reverseChars.ToArray());
}
}

כעת נוכל לקרוא למתודה בצורה postfixית:

1
string reversedString = "Hello world".Reverse();

וגם בצורה prefixית:

1
string reversedString = StringUtilities.Reverse("Hello world");

מטורף, אה?

גם אם היו לנו מספר פרמטרים יכולנו ליצור Extension method, למשל:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static string Repeat(this string givenString,
int times)
{
List<string> duplicatedStrings = new List<string>();
for (int i = 0; i < times; i++)
{
duplicatedStrings.Add(givenString);
}
return string.Join(string.Empty,
duplicatedStrings.ToArray());
}

נוכל לקרוא לה ע"י קריאה postfixית של המשתנה הראשון:

1
string repeated = "Hello world".Repeat(3);

מגניב..


קצת מגבלות:

צריך כאמור שהמתודה תהיה בתוך מחלקה סטטית. בנוסף, אסור שמחלקה זו תהיה מחלקה פנימית של מחלקה אחרת.

בנוסף, אם יש לנו בnamespace שתי extension methods עם אותו שם, לא נוכל לקרוא בpostfix.

חוץ מזה, ניתן לכתוב extension methods רק לארגומנט הראשון שלהן, כלומר את הkeyword ששמו this נוכל לשים רק לפני המשתנה הראשון.

לבסוף, אם אנחנו רוצים להשתמש בextension method מnamespace אחר, נאלץ לעשות לו using.

בסה"כ extension methods זו דרך מאוד אלגנטית לבצע דברים שהיינו עושים עד היום במחלקות Utilities למיניהן.

שיהיה שבוע מורחב טוב

שתף