49

What happens if we serialize a static class? Can more than one instance of the static class be created if we serialize it?

[Serializable]
public static class MyClass
{
    public static MyClass()
    {

    }

    public static bool IsTrue()
    {
       return true;
    }
}

Suppose I XmlSerialize the object to a XML file, and at a later time I de-serialize back to a object. Another copy exists in the memory (created when somone instintiated the static calss for the first time). Will, there be two copy of the of the object? If yes, can we stop that? Does it apply to any class which follows the singleton pattern?

Bhaskar
  • 10,537
  • 6
  • 53
  • 64

7 Answers7

55

There are never any instances of static classes: they are both abstract and sealed in the IL, so the CLR will prevent any instances being created. Therefore there is nothing to serialize. Static fields are never serialized, and that's the only sort of state that a static class can have.

Your question about XML serialization makes no sense, as no-one can ever have created an instance of the static class to start with.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    what about this: `public static class Constants { public const string Con1 = "con1"; public const string Con2 = "con2"; }` I would like to dynamically pass this to javascript, in that case some way of serializing makes sense. Yes, the word _serialize_ can have slightly different meanings today. – or hor Apr 21 '16 at 08:34
  • 1
    @orhor: What do you mean by "pass this"? If you want to pass "a collection of constants" then you could do that reasonably easily, but I don't think that changes the normal meaning of the term "serialize". – Jon Skeet Apr 21 '16 at 09:23
  • ok, literrally: I need to call a service from javascript and deserialize its response into javascript object relating to service's c# type. By the slightly different meanings I meant those two alternatives: 1. translating an _object_ (that implies an instance) into a storable format 2. translating anything - just memory data, a type or whatever you want into a text format. For example transferrable over a web from service to client but no offense, please, I am not insulting your answer, I am just (hopefully) widening the spectrum of information on that page. And thank you for questions. – or hor Apr 21 '16 at 10:20
  • 1
    @orhor: I would suggest asking a new question for your very specific situation. What you're describing sounds broadly like "conversion into a text representation" rather than "serialization". – Jon Skeet Apr 21 '16 at 10:25
20

You can't serialize static classes (or any class whatsoever) using built-in .NET serialization features. You can only serialize instances of classes.

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • But, can the above scenario exists? That is, can there be more than one instance of a static class? – Bhaskar Aug 18 '09 at 12:28
16

You may create the following class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters.Soap;
using System.Reflection;
using System.IO;

namespace SerializeStatic_NET
{
    public class SerializeStatic
    {
        public static bool Save(Type static_class, string filename)
        {
            try
            {
                FieldInfo[] fields = static_class.GetFields(BindingFlags.Static | BindingFlags.Public);
                object[,] a = new object[fields.Length,2];
                int i = 0;
                foreach (FieldInfo field in fields)
                {
                    a[i, 0] = field.Name;
                    a[i, 1] = field.GetValue(null);
                    i++;
                };
                Stream f = File.Open(filename, FileMode.Create);
                SoapFormatter formatter = new SoapFormatter();                
                formatter.Serialize(f, a);
                f.Close();
                return true;
            }
            catch
            {
                return false;
            }
        }

        public static bool Load(Type static_class, string filename)
        {
            try
            {
                FieldInfo[] fields = static_class.GetFields(BindingFlags.Static | BindingFlags.Public);                
                object[,] a;
                Stream f = File.Open(filename, FileMode.Open);
                SoapFormatter formatter = new SoapFormatter();
                a = formatter.Deserialize(f) as object[,];
                f.Close();
                if (a.GetLength(0) != fields.Length) return false;
                int i = 0;
                foreach (FieldInfo field in fields)
                {
                    if (field.Name == (a[i, 0] as string))
                    {
                        field.SetValue(null, a[i,1]);
                    }
                    i++;
                };                
                return true;
            }
            catch
            {
                return false;
            }
        }
    }
}

You must define a reference to System.Runtime.Serialization.Formatters.Soap.

Say, in your program you want to save the following static class:

public static class A
{
    public static string s;
    public static int i;
    public static double z;
}

You may use the following code:

bool ok = SerializeStatic.Save(typeof(A), "c:\\tests\\a.dat");

If you want to load the saved data (in the same program or in another program), use the following code:

bool ok2 = SerializeStatic.Load(typeof(A), "c:\\tests\\a.dat");

The fields A.s, A.i, A.z will get the new, loaded values.

Mikhail Semenov
  • 953
  • 8
  • 8
8

Why not just use a temporary instance class that is a mirror of the static class?

[XmlRoot]
public class SerializeClass
{
    public int Number {
        get;
        set;
    }
}

public static class SerializedClass {

    public static int Number {
        get;
        set;
    }


    public static void Serialize(Stream stream) {

        SerializeClass obj = new SerializeClass();
        obj.Number = Number;

        XmlSerializer serializer = new XmlSerializer(typeof(SerializeClass));
        serializer.Serialize(stream, obj);
    }

    public static void Deserialize(Stream stream) {

        XmlSerializer serializer = new XmlSerializer(typeof(SerializeClass));
        SerializeClass obj = (SerializeClass)serializer.Deserialize(stream);

        Number = obj.Number;
    }
}

I know it's a bit of a hack, but it acheives the same purpose, while still allowing Refactor before runtime, and value validation during runtime.

SilverX
  • 1,509
  • 16
  • 18
  • 1
    No explanation for the down vote? Static classes cannot be serialized, and this is an acceptable work-around, I wonder why reflection is the best answer in this scenario? It requires more resources and is much slower than simply copying the data to an object that CAN be serialized. hmmm – SilverX Aug 23 '13 at 20:49
  • The reflection route is MUCH fewer lines of code if you've got a lot of classes, or, indeed, classes with lots of fields. – LeeCambl Mar 02 '15 at 15:04
0

I found this answer realy useful for my setting class! 1000 thanks to you!

But i had to do some changes to make it working, due to a non-serializable object and change to BinaryFormatter due to servicepack compatiblity

public class SerializeStatic
{
    public static bool Save(Type static_class, string filename)
    {
        try
        {
            FieldInfo[] fields = static_class.GetFields(BindingFlags.Static | BindingFlags.Public);

            object[,] a = new object[fields.Length-1,2]; //one field can´t be serialized, so shouldn´t be counted
            int i = 0;
            foreach (FieldInfo field in fields)
            {
                if(field.Name == "db") continue; // db cant be serialized! so get away.. not very pretty but does its job :)
                a[i, 0] = field.Name;
                a[i, 1] = field.GetValue(null);
                i++;
            };
            Stream f = File.Open(filename, FileMode.Create);
            BinaryFormatter formatter = new BinaryFormatter(); //Soapformatter -> .NET 4.5 -> doesn´t run under xp!
            // SoapFormatter formatter = new SoapFormatter();
            formatter.Serialize(f, a);
            f.Close();
            return true;
        }
        catch(Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.ToString()); //Better error messages
            return false;
        }
    }

    public static bool Load(Type static_class, string filename)
    {
        try
        {
            FieldInfo[] fields = static_class.GetFields(BindingFlags.Static | BindingFlags.Public );
            object[,] a;
            Stream f = File.Open(filename, FileMode.Open);
            BinaryFormatter formatter = new BinaryFormatter();
            a = formatter.Deserialize(f) as object[,];
            f.Close();
            if (a.GetLength(0) != fields.Length-1) return false;

            foreach ( FieldInfo field in fields)  
                for(int i=0;i< fields.Length-1;i++) //I ran into problems that some fields are dropped,now everyone is compared to everyone, problem fixed
                    if (field.Name == (a[i, 0] as string))
                        field.SetValue(null, a[i,1]);
            return true;
        }
        catch(Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.ToString());
            return false;
        }
    }
}
andi
  • 1
0

Another solution but one that reads and writes in xml. You can also use the [NonSerialized] attribute above a field to prevent it from being serialized.

public static class SerializeStatic
{
    public static bool Serialize(Type staticClass, string fileName)
    {
        XmlTextWriter xmlWriter = null;

        try
        {
            xmlWriter = new XmlTextWriter(fileName, null);

            xmlWriter.Formatting = Formatting.Indented;

            xmlWriter.WriteStartDocument();

            Serialize(staticClass, xmlWriter);

            xmlWriter.WriteEndDocument();

            return true;
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.ToString());

            return false;
        }
        finally
        {
            if (xmlWriter != null)
            {
                xmlWriter.Flush();
                xmlWriter.Close();
            }
        }
    }

    public static void Serialize(string name, object obj, XmlTextWriter xmlWriter)
    {
        Type type = obj.GetType();
        XmlAttributeOverrides xmlAttributeOverrides = new XmlAttributeOverrides();
        XmlAttributes xmlAttributes = new XmlAttributes();
        xmlAttributes.XmlRoot = new XmlRootAttribute(name);
        xmlAttributeOverrides.Add(type, xmlAttributes);
        XmlSerializer xmlSerializer = new XmlSerializer(type, xmlAttributeOverrides);

        xmlSerializer.Serialize(xmlWriter, obj);
    }

    public static bool Serialize(Type staticClass, XmlTextWriter xmlWriter)
    {
        FieldInfo[] fieldArray = staticClass.GetFields(BindingFlags.Static | BindingFlags.Public);

        xmlWriter.WriteStartElement(staticClass.Name);

        foreach (FieldInfo fieldInfo in fieldArray)
        {
            if (fieldInfo.IsNotSerialized)
                continue;

            string fieldName = fieldInfo.Name;
            string fieldValue = null;
            Type fieldType = fieldInfo.FieldType;
            object fieldObject = fieldInfo.GetValue(fieldType);

            if (fieldObject != null)
            {
                if (fieldType.GetInterface("IDictionary") != null || fieldType.GetInterface("IList") != null)
                {
                    Serialize(fieldName, fieldObject, xmlWriter);
                }
                else
                {
                    TypeConverter typeConverter = TypeDescriptor.GetConverter(fieldInfo.FieldType);
                    fieldValue = typeConverter.ConvertToString(fieldObject);

                    xmlWriter.WriteStartElement(fieldName);
                    xmlWriter.WriteString(fieldValue);
                    xmlWriter.WriteEndElement();
                }
            }
        }

        xmlWriter.WriteEndElement();

        return true;
    }

    public static bool Deserialize(Type staticClass, string fileName)
    {
        XmlReader xmlReader = null;

        try
        {
            xmlReader = new XmlTextReader(fileName);

            Deserialize(staticClass, xmlReader);

            return true;
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.ToString());

            return false;
        }
        finally
        {
            if (xmlReader != null)
            {
                xmlReader.Close();
                xmlReader = null;
            }
        }
    }

    public static object Deserialize(string name, Type type, XmlReader xmlReader)
    {
        XmlAttributeOverrides xmlAttributeOverrides = new XmlAttributeOverrides();
        XmlAttributes xmlAttributes = new XmlAttributes();
        xmlAttributes.XmlRoot = new XmlRootAttribute(name);
        xmlAttributeOverrides.Add(type, xmlAttributes);
        XmlSerializer xmlSerializer = new XmlSerializer(type, xmlAttributeOverrides);

        return xmlSerializer.Deserialize(xmlReader);
    }

    public static bool Deserialize(Type staticClass, XmlReader xmlReader)
    {
        FieldInfo[] fieldArray = staticClass.GetFields(BindingFlags.Static | BindingFlags.Public);
        string currentElement = null;

        while (xmlReader.Read())
        {
            if (xmlReader.NodeType == XmlNodeType.EndElement)
                continue;

            if (xmlReader.NodeType == XmlNodeType.Element)
            {
                currentElement = xmlReader.Name;
            }

            foreach (FieldInfo fieldInfo in fieldArray)
            {
                string fieldName = fieldInfo.Name;
                Type fieldType = fieldInfo.FieldType;
                object fieldObject = fieldInfo.GetValue(fieldType);

                if (fieldInfo.IsNotSerialized)
                    continue;

                if (fieldInfo.Name == currentElement)
                {
                    if (typeof(IDictionary).IsAssignableFrom(fieldType) || typeof(IList).IsAssignableFrom(fieldType))
                    {
                        fieldObject = Deserialize(fieldName, fieldType, xmlReader);

                        fieldInfo.SetValueDirect(__makeref(fieldObject), fieldObject);
                    }
                    else if (xmlReader.NodeType == XmlNodeType.Text)
                    {
                        TypeConverter typeConverter = TypeDescriptor.GetConverter(fieldType);
                        object value = typeConverter.ConvertFromString(xmlReader.Value);

                        fieldInfo.SetValue(fieldObject, value);
                    }
                }
            }
        }

        return true;
    }
}
headkaze
  • 469
  • 4
  • 11
0

I found an interesting hack to keep a static class and enable it to make it readable by the xml serializer.

Keep your class non-static but put it in another class which is static

namespace Example
{
    static public class C
    {
        public static MyClass myClass = new MyClass();
    }
}

Then you can use the xml serializer to serialize/deserialize like this

Method :

public void WriteMyClass (string savepath, MyClass myClass)
    {
        XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyClass));
        XmlWriter xml_Writer;
        XmlWriterSettings xml_Settings = new XmlWriterSettings();

        xml_Settings.NewLineOnAttributes = true;
        xml_Settings.Indent = true;

        xml_Writer = XmlWriter.Create(savepath, xml_Settings);
        xmlSerializer.Serialize(xml_Writer, myClass);
        xml_Writer.Close();
    }

Use :

WriteMyClass(savepath, C.myClass);

I know it may not be the cleaner way, but this may combine XML Serializer and static class, but MyClass can be instantiated in other parts of your code, so be carefull about that.

Hope this may help.

Ben-to
  • 1