Browse DevX
Sign up for e-mail newsletters from DevX


Serialization and Events

Serialization process can result to problems when serializing objects that expose events. This is due to events handling in .NET. This paper describes two techniques to solve this problem, then it exposes some considerations about events and object lifecycle.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Andrea Zanetti
Riccardo Munisso

Downolad code from here


Serialization is a very powerful characteristic of the .NET Framework. Serialization make the task of object persistency very easy but there are some point to be aware when performing serialization.
Our team used serialization for a short while and found out that the following issues can lead to problems.

The first point to consider is versioning. Serialized and persistent object can become easily "obsolete" as the class of the object changes. The serialized object is very "fragile" in respect of class modifications, for example member types cannot change otherwise deserialization process will raise an exception.
The second point is a problem with serialization and events. This is the subject of the present work.

Serialization and events

Suppose you have a class which exposes an event. You build an object (called ser1) instancing that class. Then you build an object (called lst1) which respond to the events risen by object ser1.
When you serialize object ser1 the .NET Framework tries to serialize also object lst1.
This situation is very dangerous.
First, not all classes are serializable, so the most common situation is a runtime exception. This exception will appear only when the serialized object has some listener, so serialization can succeed or not "randomly". On the second hand, even if all classes are serializable you will probably serialize objects you don't want to, that is all the listeners to your object's events.

Events handling in .NET

To understand why .NET framework tries to serialize also the listeners to an object's events we should try to understand how .NET handles events. We know that multicast delegates looks like events, actually they are event.

To explore event handling we just build a very simple class and look at MSIL code generated by VB compiler

Public Class Serialize1 Public Event TestEvent(ByVal sender As Serialize1, ByVal args As Object) Public Sub GenerateEvent() RaiseEvent TestEvent(Me, Nothing) End Sub End Class

We can notice the following:

  • For each event MSIL declares a class called EventNameEventHandler which inherits from System.MulticastDelegate
    .class auto ansi sealed nested public TestEventEventHandler extends [mscorlib]System.MulticastDelegate
  • For each event MSIL declares a private field called EventNameEvent with a type of the EventNameEventHandler class.
    .field private class SerialTests.Serialize1/TestEventEventHandler TestEventEvent
  • For each event MSIL declares two functions called remove_EventName and add_EventName.
    .method public newslot specialname virtual instance void remove_TestEvent(class SerialTests.Serialize1/TestEventEventHandler obj) cil managed synchronized
    .method public newslot specialname virtual instance void add_TestEvent(class SerialTests.Serialize1/TestEventEventHandler obj) cil managed synchronized

The existence of a private field called EventNameEvent can also be noticed trying to declare a field called EventNameEvent. Compiler signals the following error:
"Private Dim TestEventEvent As TestEventEventHandler" implicitly declared for "Public Event TestEvent(sender As Serialize1, args As Object)" in class "Serialize1".

The previous points mean that the class will keep track of all its listeners through a multicast delegate called EventNameEventHandler. All listeners should then "subscribe" to the notification list for the event calling add_EventName when "connecting" to the event exposed by the class.

An object becomes a listener to the events of another object declaring a variable "with events", through "Handles" key word or through "AddHandler" instruction.

We can see the MSIL code for this cases building a simple listener class as follows.

Public Class Listener1 Private WithEvents mySer1 As Serialize1 Private myName As String Public Sub New(ByVal Ser As Serialize1, ByVal name As String) mySer1 = Ser myName = name AddHandler mySer1.TestEvent, AddressOf mySer1_TestEvent End Sub Private Sub mySer1_TestEvent(ByVal sender As SerialTests.Serialize1, _ ByVal args As Object) Console.WriteLine(myName + " Listener1.mySer1_TestEvent") End Sub Private Sub mySer1_TestEvent2(ByVal sender As SerialTests.Serialize1, _ ByVal args As Object) Handles mySer1.TestEvent Console.WriteLine(myName + " Listener1.mySer1_TestEvent") End Sub End Class

The AddHandler instruction leads to the following MSIL code

IL_001f: ldvirtftn instance void SerialTests.Listener1::mySer1_TestEvent(class SerialTests.Serialize1, object)
IL_0025: newobj instance void SerialTests.Serialize1/TestEventEventHandler::.ctor(object, native int)
IL_002a: callvirt instance void SerialTests.Serialize1::add_TestEvent(class SerialTests.Serialize1/TestEventEventHandler)

As we can see there is a call to the add_EventName method. When a field is declared WithEvents and there is a procedure with a "Handles" key word VB compiler add MSIL code in the set_FieldName method calling remove_EventName and add_EventName.

Serialization Graph

What we discovered explains why .NET framework tries to serialize all the listener of the events of an object.
When building the serialization graph of objects referenced by an object .NET framework goes though all fields of the object. In this operation .NET framework considers also the multicast delegate which keeps a reference to all listeners to the object's events.
This way all the listener of the object events are added to the serialization graph.

Solution to the serialization of object that exposes events

The serialization process should not consider all listener to an object's events in the serialization graph. This can be achieved in many ways.

  • You could clone the object before serialization. The new object will not have any listener and this will make the serialize method to work properly. This is very convenient if the object implements ICloneable interface and if the cloning process does not use serialization!.
  • You could implement ISerializable and a personal serialization, serializing all fields but not the EventNameEvent ones
  • You could detach events from the object before serialization and then reattach them to the object.

In the following sections we will explore the last two points

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date