WPF 过多的 PropertyChanged 事件

发布于 2024-09-14 06:18:11 字数 2297 浏览 5 评论 0原文

通常,在对象的属性设置器中,我们可能希望引发 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);
        }

任何想法都非常感谢。

问候, 菲兹

Typically in the property setter of an object we may want to raise a PropertyChanged event such as,

    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"); 
        }
    }

In our existing code base I see instances where PropertyChangedEventArgs is being sent null in order to indicate that all properties of the object have changed. This seems inefficient and seems to lead to far more events being triggered than is needed. It also seems to causes issues where objects update each other in a circular fashion.

Is this ever a good practice?

A comment in the code tries to justify it ...

//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);
        }

Any thoughts much appreciated.

Regards,
Fzzy

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

场罚期间 2024-09-21 06:18:11

这实际上是 PropertyChangedEventArgs 的设计(或其文档)存在问题。将 PropertyName 设置为 null 意味着“该对象上的所有属性都已更改”。但是,除非类是密封的,或者您使用反射,否则您实际上无法知道对象上的所有属性都已更改。您最多可以说的是对象基类中的所有属性都已更改。

在我的书中,这足以成为不使用这种特定约定的理由,除非在极少数情况下我创建了实现属性更改通知的密封类。

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

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

……实际意图恰恰相反。如果方法更改对象的 FooBarBazBat 属性,并且该对象具有只有四、五处房产,筹集一项活动可能比筹集四项更好。另一方面,如果对象有 60 个属性,那么引发四个事件可能会更好,使对象的每个侦听器(即使是那些不查看这四个属性的侦听器)在他们关心的属性发生更改时执行他们所做的任何操作,因为这些属性没有。

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

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

在第二个示例中,如果 Fixture 对象具有(例如)十个依赖于 FixtureStatus 值的属性,则引发十个属性更改事件可能看起来有点过多。也许是的。也许该对象应该引发一个 FixtureStatusChanged 事件。然后,了解您的应用程序域的类可以侦听这一事件并忽略 PropertyChanged 事件。 (您仍然会在其他属性上引发 PropertyChanged 事件,以便知道 FixtureStatusChanged 事件含义的对象可以保持当前状态 -也就是说,在实现 FixtureStatusChanged 后,您的类是否仍然需要实现 INotifyPropertyChanged。)

第二个注释:C# 世界中的大多数类,如果它们实现引发 Foo 事件的方法,调用该方法 OnFoo。这是一个重要的约定:它使方法和事件之间的关系变得明确,并且使调用该方法的代码引发事件的事实易于识别。 Notify 是一般方法的弱名称 - 通知谁?什么? - 在这种情况下,它实际上混淆了一些应该明确的内容。属性更改通知非常棘手,如果您的命名约定不掩盖它正在发生的事实。

This is actually a problem with the design (or its documentation) of PropertyChangedEventArgs. Setting PropertyName to null means "all properties on this object have changed." But unless the class is sealed, or you're using reflection, you can't actually know that all properties on the object have changed. The most you can say is that all of the properties in the object's base class have changed.

This is reason enough to not use this particular convention, in my book, except in the vanishingly small number of cases where I create sealed classes that implement property-change notification.

As a practical matter, what you're really trying to do is just raise one event that tells listeners "a whole bunch of properties on this object have changed, but I'm not going to bother to tell you about them one by one." When you say:

I see instances where PropertyChangedEventArgs is being sent null in order to indicate that all properties of the object have changed. This seems inefficient and seems to lead to far more events being triggered than is needed.

...the actual intent is the exact opposite. If a method changes the Foo, Bar, Baz, and Bat properties on an object, and the object has only four or five properties, raising one event is probably better than raising four. On the other hand, if the object has sixty properties, raising four events is probably better making every one of the object's listeners - even those that aren't looking at those four properties - do whatever they do when the properties that they care about change, because those properties didn't.

The problem is that the property-change notification system, as designed, isn't a fine-grained enough tool for every single job. It's designed to be completely generic, and has no knowledge of a particular application domain built into it.

And that, it seems to me, is what's missing from your design: application domain knowledge.

In your second example, if a Fixture object has (say) ten properties that depend on the value of FixtureStatus, raising ten property-change events may seem a little excessive. Maybe it is. Maybe the object should raise a FixtureStatusChanged event instead. Then classes with knowledge of your application domain can listen to this one event and ignore the PropertyChanged event. (You still raise the PropertyChanged event on the other properties, so that objects that don't know what a FixtureStatusChanged event means can stay current - that is, if it's still necessary for your class to implement INotifyPropertyChanged once you've implemented FixtureStatusChanged.)

A secondary comment: Most classes in the C# universe, if they implement a method that raises the Foo event, call that method OnFoo. This is an important convention: it makes the relationship between the method and the event explicit, and it makes the fact that the code that's calling the method is raising an event easy to recognize. Notify is a weak name for a method in general - notify who? of what? - and in this case it actually obfuscates something that should be made explicit. Property-change notification is tricky enough without your naming convention concealing the fact that it's happening.

鱼窥荷 2024-09-21 06:18:11

忽略其他内容,我认为单独使用 Notify(null) 是一种不好的做法。本质上并不清楚这意味着什么,对于工作了 5 年代码的开发人员来说,他们可能会认为这意味着其他东西,除非他们碰巧看到了这些注释。

Ignoring the other stuff, I'd say the Notify(null) alone is a bad practice. It's not inherently clear what that means, and to a developer working the code 5 years down the line would probably assume that it meant something else unless they happened upon the comments.

山人契 2024-09-21 06:18:11

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

例如

双数
{
获取{返回num;}

{

数字=值;
OnPropertyChanged("数字");
OnPropertyChanged("TwiceNumber");
}
}

double 两次数
{
得到{返回_num * 2.0;}

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

I have come across situations wherein computed properties (without setters) need to fire PropertyChangeNotification when some other property i set via a setter.

eg

double Number
{
get { return num;}
set
{

num=value;
OnPropertyChanged("Number");
OnPropertyChanged("TwiceNumber");
}
}

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

As a rule I only do it with get only properties and I don't see why in this case a property firing a change notification on the other is bad. But I think if I do it for any other case I most likely don't know what I am doing!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文