【问题标题】:and events (INotifyPropertyChanged, specifically)和事件(特别是 INotifyPropertyChanged)
【发布时间】:2011-10-05 14:04:23
【问题描述】:

我遇到了一个非常奇怪的问题,我似乎无法用一个小例子重现。抱歉,这个问题有点含糊。

我有一个包含地址的人。两者都继承自实现 INotifyPropertyChanged 的​​ BaseEntity。我希望 Person 类 NotifyPropertyChanged("Address") 不仅在设置地址时,而且在地址本身更改时,所以我在 Person 中的 get/set 看起来像这样:

class Person : BaseEntity
{
    private Address address;
    public Address Address
    {
        get { return address; }
        set
        {
            address = value;
            NotifyPropertyChanged("Address");

            // propagate changes in Address to changes in Person
            address.PropertyChanged += (s, e) => { NotifyPropertyChanged("Address"); };
        }
    }

    ...
}

这几个月来效果很好。

我已将 [Serializable] 添加到 Person、Address 和 BaseEntity(以及 [field: NonSerialized] 到 BaseEntity 的 PropertyChanged),现在当我更改 Address (somePerson.Address.Street = "something new")该地址的 PropertyChanged 的​​ invocationCount 为 0,以前为 1,因此 Person 不会收到通知,并且本身不会触发 NotifyPropertyChanged("Address");

同样,如果我从 Person 中删除 [Serializable],它会起作用,如果我将它添加回来,它就不起作用。我实际上还没有序列化任何东西,我只是添加了 [Serializable] 属性。

有什么想法吗?

【问题讨论】:

    标签: c# events inotifypropertychanged serializable


    【解决方案1】:

    我的猜测(受 cmets 讨论的启发)是您的应用程序中某处的某些代码检测 [Serializable] 并决定序列化该对象。例如,缓存可能是候选对象 - 就像任何“深度克隆”代码一样。

    尝试实现ISerializable(或者只是添加一个序列化回调),并添加一个断点。如果您的断点命中,请加载调用堆栈窗口并返回堆栈以查看正在序列化您的对象的内容以及原因。

    【讨论】:

    • 我添加了一个带有断点的 [OnSerialized] 回调,这是堆栈跟踪:dl.dropbox.com/u/4220513/stacktrace.txt。可能看起来像 WCF,谁说“哦,嘿,它是可序列化的,所以我想我现在要序列化它”,并且因为我在 BaseEntity 的 PropertyChanged 事件上有 [field: NonSerialized],事件处理程序被销毁(我猜是在反序列化时) .
    【解决方案2】:

    您的 Person/Address/BaseEntity 是否被序列化/反序列化并且 然后 表现出这种行为,或者仅仅是导致这种行为的 [Serializable] 属性的附加?

    我询问对象是否被反序列化,然后在我的大多数 INotifyPropertyChanged 实现中表现出这种行为我明确地将 PropertyChanged 事件标记为非序列化,然后在反序列化时根据需要手动重新挂钩事件。 (序列化事件会扩展对象图并可能导致意外对象的序列化。)

    如果您的对象没有序列化事件,那么在反序列化时,它们似乎不会触发是有道理的。他们可能正在长大,但没有人再听了。

    【讨论】:

    • 另外值得注意的是 BinaryFormatter 序列化字段,而不是属性...所以在反序列化时,不会调用属性设置器,也不会重新挂钩 PropertyChanged 事件。
    • 好点,我应该澄清一下,我的 cmets 来自我使用 BinaryFormatter 的经验。
    • 只需添加导致此行为的 [Serializable] 属性即可。
    • @epalm 除非涉及到一些序列化,否则我觉得很难相信......真的需要看到一个可重现的例子。我的建议 - 复制一份你损坏的版本,一次简单一点,直到它起作用 - 嘿,很快,解决了(即你会发现关键的区别)
    • @epalm - 查找的简单方法 - 实现 ISerializable(并添加适当的构造函数),并在其中放置一个断点。这会告诉你它是否被序列化。或者甚至只是添加一个序列化回调。
    【解决方案3】:

    查看是否添加:

    [field:NonSerialized]
    public event PropertyChangedEventHandler PropertyChanged;
    

    成功了。

    原文来自:How to exclude nonserializable observers from a [Serializable] INotifyPropertyChanged implementor?

    【讨论】:

    • BaseEntity 中的 PropertyChanged 事件已经有了这个属性。
    【解决方案4】:

    我做了一个小测试,让类可序列化和不可序列化之间没有区别。这是一些工作示例代码。

        [Serializable]
        public class BaseEntity : INotifyPropertyChanged
        {
            [NonSerialized]
            private PropertyChangedEventHandler _propertyChanged;
    
            public event PropertyChangedEventHandler PropertyChanged
            {
                add { _propertyChanged += value; }
                remove { _propertyChanged -= value; }
            }
    
            protected void NotifyPropertyChanged(string propertyName)
            {
                _propertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
        [Serializable]
        public class Person : BaseEntity
        {
            private Address _address;
    
            public Address Address
            {
                get { return _address; }
                set
                {
                    _address = value;
                    NotifyPropertyChanged("Address");
                    Address.PropertyChanged += (s, e) =>
                                                   {
                                                       Console.WriteLine("Address Property Changed {0}", e.PropertyName);
                                                       NotifyPropertyChanged("Address");
                                                   };
                }
            }
    
        }
        [Serializable]
        public class Address : BaseEntity
        {
            private string _city;
            public string City
            {
                get { return _city; }
                set
                {
                    _city = value;
                    NotifyPropertyChanged("City");
                }
            }
        }
        static void Main(string[] args)
        {
    
    
    
            var person = new Person();
            person.PropertyChanged += (s, e) => Console.WriteLine("Property Changed {0}", e.PropertyName);
            person.Address = new Address();
            person.Address.City = "TestCity";
    
        }
    

    程序输出是

    物业变更地址

    地址属性更改城市

    物业变更地址

    【讨论】:

    • 只需添加导致此行为的 [Serializable] 属性。我没有(积极地)序列化任何东西。
    • @epalm 我写了一个小测试应用程序,但无法重现您看到的错误。您可能想将我所做的与您的实际代码进行比较,看看是否有任何实质性差异
    猜你喜欢
    • 2012-10-28
    • 1970-01-01
    • 2012-01-18
    • 1970-01-01
    • 1970-01-01
    • 2011-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多