views:

253

answers:

2

I have a data object that is deep-cloned using a binary serialization. This data object supports property changed events, for example, PriceChanged.

Let's say I attached a handler to PriceChanged. When the code attempts to serialize PriceChanged, it throws an exception that the handler isn't marked as serializable.

My alternatives:

  • I can't easily remove all handlers from the event before serialization
  • I don't want to mark the handler as serializable because I'd have to recursively mark all the handlers dependencies as well.
  • I don't want to mark PriceChanged as NonSerialized - there are tens of events like this that could potentially have handlers. EDIT: Another reason why I can't do this is because the data classes (and hence the events) are generated and I don't have direct control over the generation code. Ideally, the generation code would just mark all events as NonSerialized.
  • Ideally, I'd like .NET to just stop going down the object graph at that point and make that a 'leaf'. So why doesn't .NET allow an entire class to be marked as NonSerialized?

--

I finally worked around this problem by making the handler implement ISerializable and doing nothing in the serialize constructor/ GetDataObject method. But, the handler still is serialized, just with all its dependencies set to null - so I had to account for that as well.

Is there a better way to prevent serialization of an entire class? That is, one that doesn't require accounting for the null dependencies?

+1  A: 

While I tend to disagree with the approach (I would simply mark the events as NonSerialized, regardless of how many there are) you could probably do this using serialization surrogates.

The idea is that you create an object that implements ISerializationSurrogate and basically does what you're already doing - nothing in the GetObjectData and SetObjectData methods. The difference is that you would be customizing the serialization of the delegate, not the class containing it.

Something like:

class DelegateSerializationSurrogate : ISerializationSurrogate {
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) {
        // do nothing
    }
    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context) {
        // do nothing
        return null;
    }
}

Then you register this with the formatter using the procedures outlined in this MSDN column. Then whenever the formatter encounters a delegate, it uses the surrogate instead of serializating the delegate directly.

Josh Einstein
Thank you for introducing me to the idea of serialization surrogates. I like it - at least I don't have to 'pollute' the original data class. One thing though - I haven't tried it yet, but I suspect that I'll still have the null dependencies problem?
ck
I wouldn't think so. Since you would be intercepting the serialization of delegates and effectively cancelling it, the handlers would not be serialized at all.
Josh Einstein
A: 

...there are tens of events...

Personally, then I'd just add the non-serialized markers, which for field-like events is most easily done via:

[field: NonSerialized]
public event SomeEventType SomeEventName;

(you don't need to add a manual backing delegate)

What are your serialization requirements exactly? BinaryFormatter is in many ways the least friendly of the serializers; the implications on events are a bit ugly, and it is very brittle if stored (IMO it is only really suitable for transport, not for storage).

However; there are plenty of good alternatives that would support most common "deep clone" scenarios:

  • XmlSerializer (but limited to public members)
  • DataContractSerializer / NetDataContractSerializer
  • protobuf-net (which includes Serializer.DeepClone for this purpose)

(note that in most of those serialization support would require extra attributes, so not much different to adding the [NonSerialized] attributes in the first place!)

Marc Gravell
The requirement is to support the deep-cloning of a data object that is constantly evolving, i.e. new fields/properties from release to release.I've noted above why marking the fields NonSerialized isn't really an option.
ck