Como excluir observadores não -semerializáveis ​​de um implementador [serializável] InotifyPropertyChanged?

StackOverflow https://stackoverflow.com/questions/618994

Pergunta

Eu tenho quase uma centena de aulas de entidade, parecendo assim:

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

Observe o [field:NonSerialized] atributo em PropertyChanged. Isso é necessário como alguns dos observadores (no meu caso - uma grade que exibe as entidades da edição) pode não ser serializável, e a entidade deve ser serializável, porque é fornecida - por meio de um aplicativo em execução em uma máquina separatora .

Esta solução funciona bem para casos triviais. No entanto, é possível que alguns dos observadores sejam [Serializable], e precisaria ser preservado. Como devo lidar com isso?

Soluções que estou considerando:

  • cheio ISerializable - A serialização personalizada requer escrever muito código, eu prefiro não fazer isso
  • usando [OnSerializing] e [OnDeserializing] atributos para serializar PropertyChanged manualmente - mas esses métodos auxiliares fornecem apenas SerializationContext, que Afaik não armazena dados de serialização (SerializationInfo isso faz isso)
Foi útil?

Solução

Você está certo que a primeira opção é mais trabalho. Embora possa potencialmente fornecer uma implementação mais eficiente, complicará muito suas entidades. Considere que se você tiver uma base Entity classe que implementa ISerializable, Todas as subclasses também precisam implementar manualmente a serialização!

O truque para fazer com que a segunda opção funcione é continuar marcando o evento como não serializável, mas ter um segundo campo que é Serializável e que você se povoa durante os ganchos de serialização apropriados. Aqui está uma amostra que acabei de escrever para mostrar como:

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;
            }
        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top