. Рассмотрим реализацию System.ComponentModel.INotifyPropertyChanged. Это очень полезный интерфейс, особенно для DataBinding. В приведённом коде рассмотрено несколько способов реализации данного интрерфейса и, как результат - выведен базовый класс реализующий INotifyPropertyChanged а также PostSharp-аттрибут...
1 using System.Collections.Generic;
2 using System.ComponentModel;
3 using System.Reflection;
4 using System;
5 using PostSharp.Extensibility;
6 using PostSharp.Laos;
7
8 public class Demo1: INotifyPropertyChanged
9 {
10 #region Вспомогательный код, может быть реализован в базовом классе
11 public event PropertyChangedEventHandler PropertyChanged;
12 protected void raisePropertyChanged(string propertyName)
13 {
14 if(PropertyChanged!=null)
15 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
16 }
17 protected void raisePropertyChanged(PropertyChangedEventArgs ea)
18 {
19 if (PropertyChanged != null)
20 PropertyChanged(this, ea);
21 }
22 #endregion
23
24 private int myProperty;
25
26 /// <summary>
27 /// Классический метод
28 /// минусом является
29 /// 1) необходимость задавать имя свойство в виде строки
30 /// 2) создание нового экземпляра объекта PropertyChangedEventArgs
31 /// при каждом изменении свойства
32 /// </summary>
33 public int MyProperty
34 {
35 get { return myProperty; }
36 set
37 {
38 myProperty = value;
39 raisePropertyChanged("MyProperty");
40 }
41 }
42
43
44 private readonly PropertyChangedEventArgs myPropertyChangedEventArgs =
45 new PropertyChangedEventArgs("MyProperty2");
46
47 /// <summary>
48 /// Более красивый/правильный метод
49 /// к сожалению более громоздкий
50 /// к тому-же всё равно приходится
51 /// использовать строковую константу
52 /// </summary>
53 public int MyProperty2
54 {
55 get { return myProperty; }
56 set
57 {
58 myProperty = value;
59 raisePropertyChanged(myPropertyChangedEventArgs);
60 }
61 }
62
63 }
64 public class Demo2 : INotifyPropertyChanged
65 {
66 #region Вспомогательный код, может быть реализован в базовом классе
67 public event PropertyChangedEventHandler PropertyChanged;
68 private static Dictionary<string,PropertyChangedEventArgs> dictionary =
69 new Dictionary<string, PropertyChangedEventArgs>();
70 protected void raisePropertyChanged(string propertyName)
71 {
72 if (PropertyChanged != null)
73 {
74 PropertyChangedEventArgs value;
75 lock (dictionary)
76 {
77 if(!dictionary.TryGetValue(propertyName, out value))
78 {
79 value = new PropertyChangedEventArgs(propertyName);
80 dictionary.Add(propertyName, value);
81 }
82 }
83 PropertyChanged(this, value);
84 }
85 }
86 protected void raisePropertyChanged(MethodBase methodBase)
87 {
88 // так как данный код вызывается из set-метода,
89 // имеющего наименование set_PropertyName
90 // поэтому просто выкусим первые 4 символа
91 raisePropertyChanged(methodBase.Name.Substring(4));
92 }
93 #endregion
94
95 private int myProperty;
96 /// <summary>
97 /// Данный метод хорош тем что экономит память
98 /// (обходимся без выделения нового экземпляра PropertyChangedEventArgs)
99 /// к сожалению опять таки без использования текстовой константы не обошлось
100 /// </summary>
101 public int MyProperty
102 {
103 get { return myProperty; }
104 set
105 {
106 myProperty = value;
107 raisePropertyChanged("MyProperty");
108 }
109 }
110 /// <summary>
111 /// Данный код позволил избавиться от использования
112 /// строковой константы. К сожалению даже для самых простых
113 /// и примитивных свойств нам все-таки приходится повторять
114 /// один и тот-же шаблон:
115 /// set
116 /// {
117 /// myProperty = value;
118 /// raisePropertyChanged(MethodBase.GetCurrentMethod());
119 /// }
120 /// </summary>
121 public int MyProperty2
122 {
123 get { return myProperty; }
124 set
125 {
126 myProperty = value;
127 raisePropertyChanged(MethodBase.GetCurrentMethod());
128 }
129 }
130 }
131 /// <summary>
132 /// А теперь "нарисуем" базовый класс,
133 /// реализующий все возможности и преимущества
134 /// предыдущих методов и дополнительно
135 /// поддерживающий PostSharp
136 /// </summary>
137 public abstract class NotifyPropertyChangedBase: INotifyPropertyChanged
138 {
139 public event PropertyChangedEventHandler PropertyChanged;
140 private static Dictionary<string, PropertyChangedEventArgs> dictionary =
141 new Dictionary<string, PropertyChangedEventArgs>();
142 protected void raisePropertyChanged(string propertyName)
143 {
144 if (PropertyChanged != null)
145 {
146 PropertyChangedEventArgs value;
147 lock (dictionary)
148 {
149 if (!dictionary.TryGetValue(propertyName, out value))
150 {
151 value = new PropertyChangedEventArgs(propertyName);
152 dictionary.Add(propertyName, value);
153 }
154 }
155 PropertyChanged(this, value);
156 }
157 }
158 protected void raisePropertyChanged(MethodBase methodBase)
159 {
160 // так как данный код вызывается из set-метода,
161 // имеющего наименование set_PropertyName
162 // поэтому просто выкусим первые 4 символа
163 raisePropertyChanged(methodBase.Name.Substring(4));
164 }
165 protected void raisePropertyChanged(PropertyChangedEventArgs ea)
166 {
167 if(PropertyChanged!=null) PropertyChanged(this, ea);
168 }
169 /// <summary>
170 /// А вот этот метод сугубо для реализации через PostSharp
171 /// </summary>
172 /// <param name="propertyName">наименование свойства</param>
173 internal void internal_raisePropertyChanged(string propertyName)
174 {
175 raisePropertyChanged(propertyName);
176 }
177 }
178 /// <summary>
179 /// Аттрибут для PostSharp-а
180 /// </summary>
181 [MulticastAttributeUsage(MulticastTargets.Property)]
182 [Serializable]
183 public sealed class NotifyThisPropertyChangedAttribute : CompoundAspect
184 {
185 public override void ProvideAspects(object element, LaosReflectionAspectCollection collection)
186 {
187 PropertyInfo pi = (PropertyInfo)element;
188 collection.AddAspect(pi.GetSetMethod(), new OnPropertySetSubAspect(pi.Name));
189 }
190 /// <summary>
191 /// Проверка допустимости применения аспекта
192 /// </summary>
193 /// <param name="element">цель аспекта</param>
194 /// <returns>допустимость применения аспекта</returns>
195 public override bool CompileTimeValidate(object element)
196 {
197 if (!base.CompileTimeValidate(element))
198 return false;
199 PropertyInfo pi = element as PropertyInfo;
200 if (pi == null)
201 return false;
202 if (!typeof(NotifyPropertyChangedBase).IsAssignableFrom(pi.DeclaringType))
203 return false;
204 if (!pi.CanWrite)
205 return false;
206 if (pi.GetSetMethod().IsStatic)
207 return false;
208 return true;
209 }
210 /// <summary>
211 /// Implementation of <see cref="OnMethodBoundaryAspect"/> that raises the
212 /// <see cref="INotifyPropertyChanged.PropertyChanged"/> event when a property set
213 /// accessor completes successfully.
214 /// </summary>
215 [Serializable]
216 private class OnPropertySetSubAspect : OnMethodBoundaryAspect
217 {
218 private readonly string propertyName;
219 /// <summary>
220 /// Initializes a new <see cref="OnPropertySetSubAspect"/>.
221 /// </summary>
222 /// <param name="propertyName">Name of the property to which this set accessor belong.</param>
223 public OnPropertySetSubAspect(string propertyName)
224 {
225 this.propertyName = propertyName;
226 }
227 /// <summary>
228 /// Executed when the set accessor successfully completes. Raises the
229 /// <see cref="INotifyPropertyChanged.PropertyChanged"/> event.
230 /// </summary>
231 /// <param name="eventArgs">Event arguments with information about the
232 /// current execution context.</param>
233 public override void OnSuccess(MethodExecutionEventArgs eventArgs)
234 {
235 NotifyPropertyChangedBase o = (NotifyPropertyChangedBase)eventArgs.Instance;
236 o.internal_raisePropertyChanged(propertyName);
237 }
238 }
239 }
240
241 /// <summary>
242 /// Демонстрируем "смешанное" использование
243 /// </summary>
244 public class PostSharpDemo1: NotifyPropertyChangedBase
245 {
246 private int prop;
247 /// <summary>
248 /// Это свойство будет отслеживаться автоматически
249 /// </summary>
250 [NotifyThisPropertyChanged]
251 public int Prop{ get { return prop; } set { prop = value; } }
252 private string strProp;
253 /// <summary>
254 /// А здесь вызовем руками
255 /// </summary>
256 public string StrProp { get { return strProp; } set
257 {
258 strProp = value;
259 raisePropertyChanged(MethodBase.GetCurrentMethod());
260 }
261 }
262 }