Comment exclure des observateurs non sérialisables d'un implémenteur [Serializable] INotifyPropertyChanged?

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

Question

J'ai presque une centaine de classes d'entités qui ressemblent à ça:

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

Notez l'attribut [champ: non sérialisé] sur PropertyChanged . Cela est nécessaire car certains des observateurs (dans mon cas - une grille affichant les entités pour l'édition) peuvent ne pas être sérialisables et l'entité doit être sérialisable, car elle est fournie - via la communication à distance - par une application s'exécutant sur une machine séparatrice. .

Cette solution fonctionne bien pour des cas triviaux. Cependant, il est possible que certains des observateurs soient [Serializable] et qu’ils aient besoin d’être préservés. Comment dois-je gérer cela?

Solutions envisagées:

  • full ISerializable - la sérialisation personnalisée nécessite l'écriture de beaucoup de code, je préférerais ne pas le faire
  • utilisant les attributs [OnSerializing] et [OnDeserializing] pour sérialiser PropertyChanged - mais ces méthodes d'assistance fournissent uniquement SerializationContext , selon les informations que AFAIK ne stocke pas les données de sérialisation ( SerializationInfo le fait)
Était-ce utile?

La solution

Vous avez raison, la première option est davantage de travail. Bien que cela puisse potentiellement vous donner une implémentation plus efficace, cela compliquera beaucoup vos entités. Notez que si vous avez une classe de base Entity qui implémente ISerializable , toutes les sous-classes doivent également implémenter manuellement la sérialisation !

L'astuce pour faire fonctionner la deuxième option consiste à continuer à marquer l'événement comme non sérialisable, mais à disposer d'un deuxième champ qui est sérialisable et à renseigner vous-même lors des raccordements de sérialisation appropriés. . Voici un exemple que je viens d'écrire pour vous montrer comment:

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;
            }
        }
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top