WPF чрезмерные события PropertyChanged - PullRequest
2 голосов
/ 12 августа 2010

Обычно в установщике свойств объекта мы можем вызвать событие PropertyChanged, например,

    public event PropertyChangedEventHandler PropertyChanged; 
    protected void Notify(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public string UserNote
    {
        get { return _userNote; }
        set
        {
            _userNote = value;
            Notify("UserNote"); 
        }
    }

В нашей существующей кодовой базе я вижу случаи, когда PropertyChangedEventArgs отправляется ноль, чтобы указать, что все свойства объекта изменились. Это кажется неэффективным и, по-видимому, приводит к запуску гораздо большего количества событий, чем необходимо. Это также вызывает проблемы, когда объекты обновляют друг друга по кругу.

Это когда-нибудь хорошая практика?

Комментарий в коде пытается оправдать его ...

//The purpose of this method is to wire up clients of NotificationBase that are also
//NotificationBases to *their* clients. Consider the following classes:     
         public class ClassA : NotificationBase
         {
             public int Foo
             {
                 get { return 123; }
                 set { Notify("Foo"); }
             }
         }

         public class ClassB : NotificationBase
         {
             ClassA A = new ClassA();
             public ClassB()
             {
                 A.PropertyChanged += AllChanged;
             }
             public void SetFoo()
             {
                 A.Foo = 456;
             }
         }

         public class ClassC
         {
             ClassB B = new ClassB();
             public ClassC()
             {
                 B.PropertyChanged += delegate { dosomething(); };
                 B.SetFoo(); // causes "dosomething" above to be called
             }
         }

        /// ClassB.SetFoo calls ClassA.Foo's setter, which calls ClassA.Notify("Foo").
        /// The event registration in ClassB's ctor causes ClassB.AllChanged to be called, which calls
        /// ClassB.Notify(null) - implying that ALL of ClassB's properties have changed.
        /// The event registration in ClassC's ctor causes the "dosomething" delegate to be called.
        /// So a Notify in ClassA is routed to ClassC via ClassB's PropertyChanged event.

        protected void AllChanged(Object sender, PropertyChangedEventArgs e)
        {
            Notify(null);
        }

Любые мысли высоко ценятся.

С уважением, Fzzy

Ответы [ 3 ]

3 голосов
/ 12 августа 2010

Это на самом деле проблема с дизайном (или его документацией) PropertyChangedEventArgs.Установка PropertyName в ноль означает «все свойства этого объекта изменились».Но если класс закрыт или вы используете отражение, вы не можете знать, что все свойства объекта изменились.Самое большее, что вы можете сказать, это то, что все свойства в базовом классе объекта изменились.

Это достаточная причина, чтобы не использовать это конкретное соглашение в моей книге, за исключением очень небольшого числа случаев, когда я создаю запечатанные классы, которые реализуют уведомление об изменении свойства.

В качестве практическоговажно то, что вы действительно пытаетесь сделать, это просто вызвать одно событие, которое сообщает слушателям «целый набор свойств этого объекта изменился, но я не собираюсь рассказывать вам о них по одному».Когда вы говорите:

, я вижу случаи, когда PropertyChangedEventArgs отправляется с нулевым значением, чтобы указать, что все свойства объекта изменились.Это кажется неэффективным и может привести к тому, что будет инициировано гораздо больше событий, чем необходимо.

... фактическое намерение - полная противоположность.Если метод изменяет свойства Foo, Bar, Baz и Bat объекта, и у объекта есть только четыре или пять свойств, повышение одного события, вероятно, лучше, чем повышение четырех.С другой стороны, если объект имеет шестьдесят свойств, вероятно, лучше поднять четыре события, заставляя каждого из слушателей объекта - даже тех, кто не смотрит на эти четыре свойства - делать то, что они делают, когда изменяются свойства, которые им нужныпотому что эти свойства этого не сделали.

Проблема в том, что система уведомлений об изменении свойств, как она была разработана, не является достаточно детализированным инструментом для каждой отдельной работы.Он спроектирован так, чтобы быть полностью универсальным, и в нем нет знаний о конкретной области приложения, встроенной в него.

И это, как мне кажется, то, чего не хватает в вашем дизайне: знание области приложения.

Во втором примере, если объект Fixture имеет, скажем, десять свойств, которые зависят от значения FixtureStatus, повышение десяти событий изменения свойств может показаться немного чрезмерным.Может быть это.Возможно, объект должен вызвать событие FixtureStatusChanged.Затем классы, обладающие знаниями в области вашего приложения, могут прослушивать это одно событие и игнорировать событие PropertyChanged.(Вы по-прежнему вызываете событие PropertyChanged для других свойств, так что объекты, которые не знают, что означает событие FixtureStatusChanged, могут оставаться актуальными, то есть если вашему классу все еще необходимореализовать INotifyPropertyChanged, как только вы реализовали FixtureStatusChanged.)

Вторичный комментарий: Большинство классов в юниверсе C #, если они реализуют метод, вызывающий событие Foo, вызывают этот метод OnFoo,Это важное соглашение: оно делает связь между методом и событием явной и делает тот факт, что код, вызывающий метод, вызывает событие, которое легко распознать.Notify это слабое название для метода в целом - уведомить кого?которого?- и в этом случае это фактически запутывает что-то, что должно быть сделано явным.Уведомление об изменении свойства достаточно сложно без соглашения об именах, скрывающего тот факт, что оно происходит.

0 голосов
/ 12 августа 2010

Я сталкивался с ситуациями, когда вычисляемые свойства (без установщиков) должны запускать PropertyChangeNotification, когда некоторые другие свойства я устанавливаю через установщик.

например

двойной номер { получить {return num;} задавать { Num = значение; OnPropertyChanged ( "Номер"); OnPropertyChanged ( "TwiceNumber"); } }

двойной TwiceNumber { get {return _num * 2.0;} }

Как правило, я делаю это только со свойствами get only, и я не понимаю, почему в этом случае свойство, вызывающее уведомление об изменении, является плохим. Но я думаю, что если я сделаю это для любого другого случая, я, скорее всего, не знаю, что я делаю!

0 голосов
/ 12 августа 2010

Игнорируя другие вещи, я бы сказал, что один Notify(null) - плохая практика.По сути, не ясно, что это значит, и разработчик, работающий с кодом 5 лет спустя, вероятно, предположит, что это означает что-то другое, если они не произошли в комментариях.

...