361. Using interception to implicitly implement INotifyPropertyChanged

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

אני רוצה לחתוך לעוד מספר דוגמאות של שימושים שיש לInterception, ולאחר מכן נחזור לדרכי מימוש.

שימוש נוסף שיש לInterception הוא מימוש של הממשק INotifyProperyChanged בחינם.

למי שלא מכיר – הממשק INotifyPropertyChanged הוא ממשק שקיים בFramework ונראה כך:

1
2
3
4
public interface INotifyPropertyChanged
{
event PropertyChangedEventHandler PropertyChanged;
}

כשתכלס PropertyChanged הוא Event שקופץ כאשר אחד הProperties של האובייקט משתנה, עם השם של הProperty שהשתנה.

הממשק הזה קיים בעיקר למטרות UI:

במידה ויש לנו UI שמציג אובייקט כלשהו, ואנחנו רוצים שהUI יתעדכן אוטומטית כשהאובייקט משתנה (ע"י Data Binding), הדרך הנכונה לעשות זאת היא לממש את הממשק INotifyPropertyChanged. הFrameworkים המוכרים לנו (Windows Form וWPF) ידעו כבר להרשם לאירוע ולעדכן את הUI בהתאם, במידה ונגדיר את הBinding נכון.

איך מממשים את הממשק הזה?

בסה"כ המימוש הוא יחסית פשוט:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class Person : INotifyPropertyChanged
{
#region Data Members
private int mAge;
private string mName;
#endregion
#region Properties
public string Name
{
get
{
return mName;
}
set
{
string originalValue = mName;
mName = value;
if (originalValue != value)
{
RaisePropertyChanged("Name");
}
}
}
public int Age
{
get
{
return mAge;
}
set
{
int originalValue = mAge;
mAge = value;
if (originalValue != value)
{
RaisePropertyChanged("Age");
}
}
}
#endregion
#region Private Methods
private void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
#endregion
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}

למי שאין כוח לקרוא: פשוט בSetterים של Properties בודקים אם באמת השתנה הערך של הProperty, ובמידה וכן – מקפיצים את הEvent.


כפי שאנחנו רואים המימוש הנ"ל הוא יחסית טכני, והיה נחמד אם היה אפשר לממש אותו בצורה אוטומטית למשתמש.

פה נכנס העניין של Interception – נוכל לכתוב Interceptor שיודע להזריק את הקוד הזה לProperties שלנו.

איך זה יראה? בערך ככה:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class NotifyPropertyChangedInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
PropertyInfo propertyInfo =
invocation.Method.GetProperty();
// Imaginary extension method
if (propertyInfo == null)
{
invocation.Proceed();
}
else
{
object originalValue =
propertyInfo.GetValue(invocation.InvocationTarget, null);
invocation.Proceed();
if (!Equals(originalValue, invocation.Arguments[0]))
{
RaisePropertyChanged(propertyInfo.Name);
}
}
}
}

זה Interceptor שעושה בערך את הקוד שראינו למעלה בProperties.

השאלה המתבקשת היא – מה עושה הפונקציה RaisePropertyChanged?

היינו רוצים שהיא תקפיץ Event, אבל זה קצת מסובך, כי מי אמר שהטיפוס שלנו בכלל מממש את INotifyPropertyChanged?

אז מה שאפשר לעשות זה את הדבר הבא:

ליצור Proxy לטיפוס שלנו שגם יממש את INotifyPropertyChanged, ולאחר מכן להתקין עליו את הInterception. זה קצת מסובך, אבל זה פתרון אפשרי.

אפשר גם ליצור Interface שיש לו פונקציה שמקפיצה את הEvent ולהכריח את מי שאנחנו עושים לו Proxy לממש אותו.


בהמשך נראה עוד Interceptorים ואחר כך נחזור למימושים אפשריים.

המשך יום שמודיע שהמאפיין השתנה טוב.

שתף