【问题标题】:How to exclude nonserializable observers from a [Serializable] INotifyPropertyChanged implementor?如何从 [Serializable] INotifyPropertyChanged 实现者中排除不可序列化的观察者?
【发布时间】:2010-10-11 18:21:17
【问题描述】:

我有将近一百个看起来像这样的实体类:

[Serializable]
public class SampleEntity : INotifyPropertyChanged
{
    private string name;
    public string Name
    {
        get { return this.name; }
        set { this.name = value; FirePropertyChanged("Name"); }
    }

    [field:NonSerialized]
    public event PropertyChangedEventHandler PropertyChanged;

    private void FirePropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this,
                new PropertyChangedEventArgs(propertyName));
    }
}

注意PropertyChanged 上的[field:NonSerialized] 属性。这是必要的,因为某些观察者(在我的情况下 - 显示要编辑的实体的网格)可能无法序列化,并且实体必须是可序列化的,因为它是通过远程处理 - 由运行在单独机器上的应用程序提供的.

此解决方案适用于琐碎的情况。但是,可能有一些观察者是[Serializable],需要保留。我该如何处理?

我正在考虑的解决方案:

  • 完整的ISerializable - 自定义序列化需要编写大量代码,我不想这样做
  • 使用[OnSerializing][OnDeserializing] 属性手动序列化PropertyChanged - 但这些辅助方法仅提供SerializationContext,AFAIK 不存储序列化数据(SerializationInfo 这样做)

【问题讨论】:

    标签: c# serialization remoting inotifypropertychanged


    【解决方案1】:

    你是对的,第一个选项是更多的工作。虽然它可能会为您提供更有效的实施,但它会使您的实体复杂化很多。考虑一下,如果您有一个实现ISerializable 的基类Entity所有子类也必须手动实现序列化

    让第二个选项起作用的诀窍是继续将事件标记为不可序列化,但要有第二个字段可序列化的,并且您在适当的序列化挂钩期间填充自己.这是我刚刚编写的一个示例,旨在向您展示如何:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var entity = new Entity();
                entity.PropertyChanged += new SerializableHandler().PropertyChanged;
                entity.PropertyChanged += new NonSerializableHandler().PropertyChanged;
    
                Console.WriteLine("Before serialization:");
                entity.Name = "Someone";
    
                using (var memoryStream = new MemoryStream())
                {
                    var binaryFormatter = new BinaryFormatter();
                    binaryFormatter.Serialize(memoryStream, entity);
                    memoryStream.Position = 0;
                    entity = binaryFormatter.Deserialize(memoryStream) as Entity;
                }
    
                Console.WriteLine();
                Console.WriteLine("After serialization:");
                entity.Name = "Kent";
    
                Console.WriteLine();
                Console.WriteLine("Done - press any key");
                Console.ReadKey();
            }
    
            [Serializable]
            private class SerializableHandler
            {
                public void PropertyChanged(object sender, PropertyChangedEventArgs e)
                {
                    Console.WriteLine("  Serializable handler called");
                }
            }
    
            private class NonSerializableHandler
            {
                public void PropertyChanged(object sender, PropertyChangedEventArgs e)
                {
                    Console.WriteLine("  Non-serializable handler called");
                }
            }
        }
    
        [Serializable]
        public class Entity : INotifyPropertyChanged
        {
            private string _name;
            private readonly List<Delegate> _serializableDelegates;
    
            public Entity()
            {
                _serializableDelegates = new List<Delegate>();
            }
    
            public string Name
            {
                get { return _name; }
                set
                {
                    if (_name != value)
                    {
                        _name = value;
                        OnPropertyChanged("Name");
                    }
                }
            }
    
            [field:NonSerialized]
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
            {
                var handler = PropertyChanged;
    
                if (handler != null)
                {
                    handler(this, e);
                }
            }
    
            protected void OnPropertyChanged(string propertyName)
            {
                OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
            }
    
            [OnSerializing]
            public void OnSerializing(StreamingContext context)
            {
                _serializableDelegates.Clear();
                var handler = PropertyChanged;
    
                if (handler != null)
                {
                    foreach (var invocation in handler.GetInvocationList())
                    {
                        if (invocation.Target.GetType().IsSerializable)
                        {
                            _serializableDelegates.Add(invocation);
                        }
                    }
                }
            }
    
            [OnDeserialized]
            public void OnDeserialized(StreamingContext context)
            {
                foreach (var invocation in _serializableDelegates)
                {
                    PropertyChanged += (PropertyChangedEventHandler)invocation;
                }
            }
        }
    }
    

    【讨论】:

    • invocation.Target 可以为空(对于匿名委托),请务必检查这一点
    • 这些事件对 XmlSerializer 没有影响(我很好,我只需要远程处理)。
    • OnSerializing 和 OnDeserialized 应该是私有的,不需要暴露它们。
    • _serializableDelegates 可以在 OnDeserialized 和 OnSerialized 中清除以减少内存使用(而不是在 OnSerializing 中清除)。
    • 我必须记住反序列化的对象图包含新对象,旧的引用不再起作用。哦!
    猜你喜欢
    • 2020-09-24
    • 1970-01-01
    • 1970-01-01
    • 2022-12-14
    • 2012-05-11
    • 1970-01-01
    • 2020-03-15
    • 1970-01-01
    • 2011-12-21
    相关资源
    最近更新 更多