193. The difference between events and delegates

רובנו מכירים את המושג ששמו Delegate מעולם הC# - זהו אובייקט המייצג מצביע לפונקציה כלשהי.

הטיפוס הזה מאפשר לנו לעשות דברים מאוד חזקים וראינו דוגמאות בעבר (למשל, טיפים על Anonymous delegates – מספרים 41-45).

אנחנו מכירים גם את המושג ששמו Event, אבל האם אי פעם תהיתם מה ההבדל בין Event לDelegate?

ובכן Event הוא סוג של Delegate, אלא שהקומפיילר מגביל את השימוש בו.

מה זה אומר? Delegate הוא טיפוס C#י לכל דבר, וזה אומר שאנחנו יכולים להחזיק Reference אליו, לשלוח אותו לפונקציה, להפעיל מתודה שלו ובעצם לעשות איתו כמעט כל דבר שאנחנו יכולים לעשות עם כל אובייקט C# אחר.

בפרט אם יש לנו Member שהוא מטיפוס Delegate כלשהו, אנחנו יכולים להפוך אותו להוסיף ולהסיר מתודות שהוא יקרא להן ע”י האופרטורים += ו-=.

לעומת זאת, לEvent הקומפיילר מתייחס בצורה אחרת – בתוך המחלקה הEvent הוא Delegate לכל דבר, אנחנו יכולים לעשות איתו כמעט כל מה שאנחנו יכולים לעשות עם כל אובייקט C#י אחר.

אלא שמבחינת העולם החיצון, כלומר טיפוסים שאינם המחלקה שלנו (אפילו אם הם יורשים ממנה), אנחנו יכולים לבצע בדיוק שתי פעולות: הרשמה לEvent והסרת רישום מEvent.

לדוגמה, נניח שיש לנו את המחלקה הזאת:

1
2
3
4
public class EventRaiser
{
public event EventHandler Raised;
}

אז בתוך המחלקה נוכל לעשות עם הEvent מה שבא לנו:

1
2
3
4
5
6
7
8
9
10
11
public class EventRaiser
{
public event EventHandler Raised;
private void WhoKnew()
{
MethodInfo raisedMethod = this.Raised.Method;
this.Raised(this, EventArgs.Empty);
this.Raised = null;
}
}

מחוץ למחלקה לעומת זאת, אף שורה בפונקציה זו לא תתקמפל:

1
2
3
4
5
EventRaiser raiser = new EventRaiser();
MethodInfo raisedMethod = raiser.Raised.Method;
raiser.Raised(raiser, EventArgs.Empty);
raiser.Raised = null;
EventHandler raisedEvent = raiser.Raised;

נקבל שגיאת קימפול

The event ‘EventRaiser.Raised’ can only appear on the left hand side of += or -= (except when used from within the type ‘EventRaiser’)

כלומר, הקומפיילר מאפשר לנו רק להוסיף או להסיר רישומים לEvent.

הדבר דומה להבדל בין Property לField.

בתוך המחלקה אנחנו יכולים לעשות עם הField שלנו מה שבראש שלנו. ברגע שאנחנו מייחצנים Property, אנחנו יותר מוגבלים. למשל, אנחנו יכולים למנוע שינוי של הProperty, אלא רק לאפשר קריאה.


בכל אופן חשוב לשים לב לדברים הבאים:

בתוך המחלקה שלו, Event הוא Delegate לכל דבר. בין השאר, אפשר גם לאפס את הערך שלו, וכאשר הinstance שלנו מת אנחנו יכולים לאפס את הEvent בתוך המחלקה, כך שאין צורך לדאוג שכל מי שנרשם אלינו יסיר את הרישום וכו’.

בנוסף, Event זה בסה"כ הכמסה של הקומפיילר – כאשר אנחנו משתמשים בEvent, מאחורי הקלעים יש לנו Delegate, כך שמבחינה טכנית, אין דבר שאפשר לעשות עם Event ואי אפשר לעשות עם Delegate. טעות נפוצה היא לחשוב שDelegate מאפשר רישום רק למתודה אחת ואילו Event מאפשר רישום לכמה מתודות. זה לא נכון – גם Delegate מאפשר רישום לכמה מתודות. (בעזרת האופרטור +=, או לחלופין בעזרת שימוש בפונקציה Delegate.Combine היוצרת Delegate חדש שקורא לכל הDelegateים אחד אחרי השני)

המשך יום מלא אירועים טובים

שתף