Singletons
Everyone is writing singletons and everyone thinks they know what they are doing, but what if they're wrong?
There is at least one scenario where things indeed can go wrong: serialization! SimoneB wrote about it in a blog post but I think she missed something. In fact, in .NET, there are at least two possible approaches to serialization:
-
One is using the classes that implement interface System.Runtime.Serialization.IFormatter, such as System.Runtime.Serialization.Formatters.Binary.BinaryFormatter or System.Runtime.Serialization.Formatters.Soap.SoapFormatter;
-
Other is using System.Xml.Serialization.XmlSerializer.
While the two are similar - in fact, SoapFormatter and XmlSerializer can produce the same output - the way they work, the interfaces and attributes they rely on, are quite different:
-
IFormatter-implementers require serializable classes to have attribute [Serializable] and can use the interface System.Runtime.ISerializable to control the serialization process, in a way similar to Java's java.lang.Externalizable interface; in a nutshell, if a class implements this interface, it controls what effectively is serialized, so it's use can produce completely unexpected results;
-
XmlSerializer class does not require the [Serializable] attribute and uses interface System.Xml.Serialization.IXmlSerializable to achieve the same purpose: control the serialization process.
I am assuming you know about singletons; to recall, the singleton pattern, as described in the famous Design Patterns book, is a software technique implemented to allow only a single, well known, object instance of a class. Let's start from the beginning, with a simple singleton class, shall we?
public sealed class Singleton { private static readonly Singleton instance = new Singleton(); private Singleton() { } public static Singleton Instance { get { return (instance); } } }
As you can see, this class cannot be derived from since it's sealed, cannot be instantiated (except if using reflection) because it has a private constructor, and can only be accessed through its static property Instance. But if we serialize it and afterwards deserialize it, we end up with two different instances of this class! This happens because .NET does not have any intrinsic support for singletons, they are written using common OOP techniques, however, it does have intrinsic support for serialization.
Using IFormatter-implementing classes does not work, because the class is not marked with the [Serializable] attribute, so we are protected from that side, but XmlSerializer does not require the use of this attribute in order to allow serialization, so we have a problem. XmlSerializer does, however, rely on interface IXmlSerializable, so if it is implemented, it's methods are called in the course of the serialization process. If we don't want our class to be serialized, we just have to do something on each of this methods that will prevent it from happening: throw an exception!
Here is the protected version of our code:
public sealed class Singleton: IXmlSerializable { private static readonly Singleton instance = new Singleton(); private Singleton() {} public static Singleton Instance { get { return (instance); } } XmlSchema IXmlSerializable.GetSchema() { throw(new NotSupportedException()); } void IXmlSerializable.ReadXml(XmlReader reader) { throw(new NotSupportedException()); } void IXmlSerializable.WriteXml(XmlWriter writer) { throw(new NotSupportedException()); } }
And that's it! This way, there is no easy way to have two instances of the Singleton class, unless, of course, if using reflection: the two basic serialization methods supported by .NET both fail.
Feedback, including disagreement, is welcome! :-)