2

通常在对象的属性设置器中,我们可能希望引发 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 被发送 null 以指示对象的所有属性都已更改的实例。这似乎效率低下,并且似乎导致触发的事件比需要的要多得多。它似乎也会导致对象以循环方式相互更新的问题。

这是一个很好的做法吗?

代码中的注释试图证明它是合理的......

//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

4

3 回答 3

3

这实际上是PropertyChangedEventArgs. 设置PropertyName为 null 意味着“此对象上的所有属性都已更改”。但除非该类是密封的,或者您正在使用反射,否则您实际上无法知道对象上的所有属性都已更改。最多只能说对象的基类中的所有属性都发生了变化。

这就是在我的书中不使用这个特定约定的充分理由,除非在极少数情况下我创建了实现属性更改通知的密封类。

实际上,你真正想做的只是引发一个事件,告诉听众“这个对象的一大堆属性已经改变,但我不会费心一一告诉你。 " 当你说:

我看到了将 PropertyChangedEventArgs 发送为 null 以指示对象的所有属性都已更改的实例。这似乎效率低下,并且似乎导致触发的事件比需要的要多得多。

...实际意图恰恰相反。如果一个方法改变了一个对象的FooBarBazBat属性,而该对象只有四个或五个属性,那么引发一个事件可能比引发四个更好。另一方面,如果对象有 60 个属性,那么引发四个事件可能会更好地使对象的每个侦听器——即使是那些没有查看这四个属性的侦听器——在他们关心的属性发生变化时执行他们所做的任何事情,因为那些属性没有。

问题在于,按照设计,属性更改通知系统对于每一项工作来说都不是一个足够细粒度的工具。它被设计为完全通用的,并且不了解内置的特定应用程序域。

在我看来,这就是您的设计中缺少的东西:应用程序领域知识。

在您的第二个示例中,如果一个Fixture对象具有(例如)十个取决于 值的FixtureStatus属性,则引发十个属性更改事件可能看起来有点过分。也许是的。也许该对象应该引发一个FixtureStatusChanged事件。然后,了解您的应用程序域的类可以监听这一事件并忽略该PropertyChanged事件。(您仍然在其他属性上引发PropertyChanged事件,以便知道FixtureStatusChanged事件意味着什么的对象可以保持最新状态 - 也就是说,如果您的类在您实现INotifyPropertyChanged后仍然需要实现FixtureStatusChanged。)

次要评论:C# 世界中的大多数类,如果它们实现了引发Foo事件的方法,请调用该方法OnFoo。这是一个重要的约定:它使方法和事件之间的关系明确,并且使调用方法的代码引发事件的事实易于识别。 Notify通常是一个方法的弱名称 - 通知谁?什么?- 在这种情况下,它实际上混淆了应该明确的东西。如果没有您的命名约定隐藏它正在发生的事实,属性更改通知就足够棘手了。

于 2010-08-12T19:08:53.277 回答
0

忽略其他东西,我会说Notify(null)单独是一种不好的做法。本质上并不清楚这意味着什么,并且对于一个工作了 5 年的代码的开发人员来说,可能会认为它意味着其他东西,除非他们发生在评论上。

于 2010-08-12T18:12:30.990 回答
0

当我通过设置器设置其他一些属性时,我遇到过计算属性(没有设置器)需要触发 PropertyChangeNotification 的情况。

例如

双数 { 获取 { 返回数;} 设置 {数 = 值;OnPropertyChanged("数字"); OnPropertyChanged("TwiceNumber"); } }

double TwiceNumber { get {return _num * 2.0;} }

通常,我只使用仅获取属性来执行此操作,并且我不明白为什么在这种情况下,触发另一个更改通知的属性是不好的。但我想如果我为其他任何情况做这件事,我很可能不知道我在做什么!

于 2010-08-12T18:16:39.077 回答