• Авторизация


Грамотная реализация System.ComponentModel.INotifyPropertyChanged 26-11-2007 20:21 к комментариям - к полной версии - понравилось!


В продолжение темы про PostSharp. Рассмотрим реализацию 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 }
вверх^ к полной версии понравилось! в evernote
Комментарии (1):


Комментарии (1): вверх^

Вы сейчас не можете прокомментировать это сообщение.

Дневник Грамотная реализация System.ComponentModel.INotifyPropertyChanged | dimzon541 - Поток не замутненного разумом сознания... | Лента друзей dimzon541 / Полная версия Добавить в друзья Страницы: раньше»