2009-03-06 8 views
12

Ich habe fast hundert Entitätsklassen wie die Suche:Wie können nichtserialisierbare Beobachter von einem [Serializable] INotifyPropertyChanged-Implementor ausgeschlossen werden?

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

Mitteilung über das [field:NonSerialized] Attribut auf PropertyChanged. Dies ist notwendig, da einige der Beobachter (in meinem Fall ein Gitter, das die Entitäten für die Edition anzeigt) möglicherweise nicht serialisierbar sind und die Entität serialisierbar sein muss, weil sie - per Remoting - von einer Anwendung bereitgestellt wird, die auf einer separater Maschine läuft .

Diese Lösung eignet sich für triviale Fälle. Es ist jedoch möglich, dass einige der Beobachter [Serializable] sind und beibehalten werden müssten. Wie soll ich damit umgehen?

Lösungen Ich erwäge:

  • voll ISerializable - benutzerdefinierte Serialisierung erfordert viel Code zu schreiben, würde ich es vorziehen, nicht diese
  • mit [OnSerializing] und [OnDeserializing] zu tun Attribute PropertyChanged manuell serialisiert werden - aber diejenigen, Hilfsmethoden bieten nur SerializationContext, die AFAIK nicht Serialisierung Daten speichert (SerializationInfo das tut)

Antwort

12

Sie sind righ t, dass die erste Option mehr Arbeit ist. Während es Ihnen möglicherweise eine effizientere Implementierung geben kann, wird es Ihre Entitäten viel verkomplizieren. Berücksichtigen Sie, dass bei einer Basisklasse Entity, die ISerializable, implementiert, auch alle Unterklassen die Serialisierung manuell implementieren müssen!

Der Trick, um die zweite Option bekommen, zu arbeiten, ist als nicht-serializable Kennzeichnung die Veranstaltung fortzusetzen, aber ein zweites Feld zu haben, dass ist serializable und dass Sie sich in dem entsprechenden Serialisierung Haken füllen. Hier ist ein Beispiel ich gerade geschrieben hat, Ihnen zu zeigen, wie:

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

invocation.Target kann Null sein (für anonyme Delegaten), achten Sie darauf, dies zu überprüfen – skolima

+0

Diese Ereignisse haben keine Auswirkungen auf XmlSerializer (gut, ich, ich brauchte nur die Remoting zu arbeiten). – skolima

+0

OnSerializing und OnDeserialized sollten privat sein und müssen nicht verfügbar gemacht werden. – skolima

1

Vereinbaren Sie mit Rich-L. [field: NonSerialized] löst ein Problem mit einem Objekt mit einem Objekt Serialisierung geändert Benachrichtigung gebaut in Snippet von Gold sicher. . Danke

Verwandte Themen