How to exclude nonserializable observers from a [Serializable] INotifyPropertyChanged implementor?

你。 提交于 2019-11-29 13:23:30

问题


I have almost a hundred of entity classes looking like that:

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

Notice the [field:NonSerialized] attribute on PropertyChanged. This is necessary as some of the observers (in my case - a grid displaying the entities for edition) may not be serializable, and the entity has to be serializable, because it is provided - via remoting - by an application running on a separater machine.

This solution works fine for trivial cases. However, it is possible that some of the observers are [Serializable], and would need to be preserved. How should I handle this?

Solutions I am considering:

  • full ISerializable - custom serialization requires writing a lot of code, I'd prefer not to do this
  • using [OnSerializing] and [OnDeserializing] attributes to serialize PropertyChanged manually - but those helper methods provide only SerializationContext, which AFAIK does not store serialization data (SerializationInfo does that)

回答1:


You're right that the first option is more work. While it can potentially give you a more efficient implementation, it will complicate your entities a lot. Consider that if you have a base Entity class that implements ISerializable, all subclasses also have to manually implement serialization!

The trick to getting the second option to work, is to continue marking the event as non-serializable, but to have a second field that is serializable and that you populate yourself during the appropriate serialization hooks. Here is a sample I just wrote to show you how:

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


来源:https://stackoverflow.com/questions/618994/how-to-exclude-nonserializable-observers-from-a-serializable-inotifypropertych

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!