Quantcast
Channel: Geekswithblogs.net | design pattern Posts
Viewing all articles
Browse latest Browse all 50

Serializing Singleton Objects (C#)

$
0
0

 

You know that when you serialize an object and then deserialize the main object and the deserialized one are different objects (different references).

So what if you do not intend such behavior i.e in a singleton class.

I’m almost certain that everyone is familiar with singleton pattern that solves the problem of having only 1 instance of a class in the entire application; so I’m not giving a speech about that here.

 

Assume we have this singleton class:

 

public class Singleton
    {
        private Singleton()
        {

        }

        private static Singleton _Instance;
        static Singleton()
        {
            _Instance = new Singleton();
        }
        public static Singleton Instance { get { return _Instance; } }
    }

 

so according to MSDN this would be the way to serialize such a singleton:

 

[Serializable]
    public class Singleton : ISerializable
    {
        private Singleton()
        {

        }

        private static Singleton _Instance;
        static Singleton()
        {
            _Instance = new Singleton();
        }
        public static Singleton Instance { get { return _Instance; } }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.SetType(typeof(SingletonHelper));
        }
        [Serializable]
        private class SingletonHelper : IObjectReference
        {
            public object GetRealObject(StreamingContext context)
            {
                return Singleton.Instance;
            }
        }

    }

 

What happened?

When implementing the interface ISerializable, you should add all necessary data to SerializationInfo object passed to method GetObjectData by calling AddValue method on it. A singleton class requires no additional data to be passed. But it has another problem, when deserializing, first of all a new instance of the target class will be created. This is bad. but the good news here is that you can tell the deserializer to create another type instead. If the other type implements IObjectReference interface, GetReadObject method would be called on it. There you can return the only instance of the singleton class.

 

 

That was somehow easy, so what is this blog saying at all?? Another problem arises when your singleton class can have more than 1 object. For example in an Enumeration Class

 

So now lets change our class a little bit to have 2 instances:

static Singleton()
        {
            Instance1 = new Singleton();
            Instance2 = new Singleton();
        }
        public static readonly Singleton Instance1;
        public static readonly Singleton Instance2;

 

now which instance should be returned when GetRealObject method of our helper class is called?

Here is the trick :

 

public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.SetType(typeof(SingletonHelper));
            var index = (this == Instance1) ? (byte)1 : (byte)2;
            info.AddValue("Index", index);
        }

 

What now?

When deserializing a class implements ISerializable, the deserializer looks for a constructor that recieves SerializationInfo info and StreamingContext context.

so if the helper class implements ISerializable, it can recieve the SerializationInfo required to decide which instance of the singleton class to return.

 

[Serializable]
        private class SingletonHelper : IObjectReference, ISerializable
        {
            private byte Index;
            public SingletonHelper(SerializationInfo info, StreamingContext context)
            {
                Index = info.GetByte("Index");
            }
            public object GetRealObject(StreamingContext context)
            {
                return Index == 1 ? Singleton.Instance1 : Singleton.Instance2;
            }

            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                throw new InvalidOperationException("Object of type SingletonHelper should not be serialized directly");
            }
        }

 

This code does all the job.

 

 

What is learned here?

A very nice pattern that is implemented in .NET serialization (note: this whole thing does not work in XmlSerializer class).

When you are saving and restoring an object’s state using reflection, this would be nice to let the object decide which states need to be saved, and it would be nice to let it stay singleton by changing the ObjectType to be created when restoring the data and act as a Proxy for deserialization.

This is one of many hidden pattern that can be distracted from nice implemented codes like Microsoft .Net Framework’s codes.


Viewing all articles
Browse latest Browse all 50

Trending Articles