Serialization is a beautiful thing. Now that was a geeky comment.... Jokes aside, it is. Standard .NET serialization is quite simple. Tag your class with the [Serializable()] attribute, make sure all your state variables are serializable and voila. But what if some of these variables aren’t serializable? Then you simply need to tag those variables as [NonSerializable()] and then you are really done. Of course I could get into the details of all this but this post isn’t about standard .NET serialization.
Custom serialization on the other hand is a little bit trickier. Not only do you have to go through all the same process as standard .NET serialization but, since your class or a class in your class hierarchy implemented ISerializable, now you got to do even more stuff just to get your states to travel from point A to point B.... So here’s the procedure to get it to work:
1) Do you have states (variables) in your class? If not, then go to step 4 and you’re almost done... Lucky you! Each class in the class hierarchy is responsible for managing its states and you should not take care of the state variables of your base classes. Mind your own business :)
2) Override GetObjectData and call the base method inside there... I personally prefer calling it on entering the method.
3) Inside the GetObjectData method, you need to “store” the states that you want to transfer from point A to point B. You do this by adding entries to the variable named “info”. This variable is passed to GetObjectData by the CLR. Parameter “info” is of type “SerializationInfo” and resembles a “NameValueCollection” in its usage. Simply call “info.AddValue” with the overload of your choice to add each state variable of the class you want to serialize and it will be added to the serialization process. You can imagine this operation was like telling the CLR which states of your class needed to be serialized. Your code should look like the following:
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("currentUser", this.currentUser);
info.AddValue("runningAs", this.runningAs);
}
4) Now that the CLR knows what to serialize, it needs to know what to do on the other end of the process, once it reaches point B... We have to tell him how to reconstruct your class’s states. Remember it’s not the class itself that was sent on the wire, it was only its states. On the other side of the wire, the CLR will create a NEW class of the same type as yours and you need to tell him which states goes to which variable. In .NET we do not inherit constructors from our base classes, we need to redefine them. In our case, one very important constructor needs to be redefined and also needs to call his matching constructor (chaining) in the base class. Its signature is the following:
protected MyClass(SerializationInfo info, StreamingContext context) : base(info, context)
You should definitely keep the “protected” modifier in place. Why? Well, because this constructor is not meant to be called by you except when you call it from a base class. This constructor is reserved for the CLR to use and only it has all the information needed to inject the right thing at the right place once we’re at point B.
5) Once the constructor redefined and its matching base class constructor called, take a closer look at the first parameter of the constructor... The CLR will inject the “info” variable into the constructor. This variable contains all the states you and your base classes added to it a few moments earlier on the original instance when the CLR called GetObjectData prior to serializing the class at point A. Then you can simply transfer each value from the “info” variable back to the state variable that holds it in the new instance of the class. It should look something like this:
protected MyClass(SerializationInfo info, StreamingContext context) : base(info, context)
{
this.currentUser = info.GetString("currentUser");
this.runningAs = info.GetString("runningAs");
}
6) This step is the most important step of all. Test your serialization!!!! I’ve build a nice little helper class for this and I’ll give it to you all so that you have no more excuses not to test your serialization prior to going live. Those of you who know me are already thinking “whaaaat? Normally this guy hates helper classes so why not use an extension method instead?”. Alright but to extend which type? ISerializable? Sure, then you can use it on some classes but not all classes because you cannot define an extension method based on metadata like the [Serializable()] attribute. It would have been nice tho J. So use the helper to serialize and deserialize a class instance and compare ALL states one by one. This is best done in a unit test by the way. You would be sure to test all your class serializations in every build of your product. In the helper, call “TestObjectSerialisation” and catch the returned object. This method serializes the instance of the class you give it and then deserializes it in memory prior to returning it to you, ready to be compared to the original instance.
I hope this post helped you understand the basics of how implementing ISerializable works and what needs to be done when you do so. For your information, one very common place where you need to do this is when you create your custom exceptions. The type "Exception" implements ISerializable... Happy coding !