¿Cómo excluir a los observadores no serializables de un implementador [Serializable] INotifyPropertyChanged?

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

Pregunta

Tengo casi un centenar de clases de entidades con ese aspecto:

[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 el atributo [field: NonSerialized] en PropertyChanged . Esto es necesario ya que algunos de los observadores (en mi caso, una cuadrícula que muestra las entidades para la edición) pueden no ser serializables, y la entidad debe ser serializable, ya que se proporciona mediante una aplicación remota en una máquina separada. .

Esta solución funciona bien para casos triviales. Sin embargo, es posible que algunos de los observadores sean [Serializable] y que deban conservarse. ¿Cómo debo manejar esto?

Soluciones que estoy considerando:

  • ISerializable completo: la serialización personalizada requiere escribir un montón de código, prefiero no hacerlo
  • utilizando los atributos [OnSerializing] y [OnDeserializing] para serializar PropertyChanged manualmente, pero esos métodos auxiliares solo proporcionan SerializationContext , que AFAIK no almacena datos de serialización ( SerializationInfo hace eso)
¿Fue útil?

Solución

Tienes razón en que la primera opción es más trabajo. Si bien puede proporcionarle una implementación más eficiente, complicará mucho sus entidades. Tenga en cuenta que si tiene una clase base Entity que implementa ISerializable , todas las subclases también tienen que implementar manualmente la serialización !

El truco para hacer que la segunda opción funcione, es seguir marcando el evento como no serializable, pero tener un segundo campo que es serializable y que usted se complete durante los ganchos de serialización apropiados. . Aquí hay una muestra que acabo de escribir para mostrarte cómo:

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top