web-dev-qa-db-fra.com

Sérialiser une classe statique?

Que se passe-t-il si nous sérialisons une classe statique? Peut-on créer plus d'une instance de la classe statique si nous la sérialisons?

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

    }

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

Supposons que je XmlSérialise l'objet dans un fichier XML et, ultérieurement, je désérialise à nouveau vers un objet. Une autre copie existe dans la mémoire (créée lorsque quelqu'un a initié les cals statiques pour la première fois). Y aura-t-il deux copies de l'objet? Si oui, pouvons-nous arrêter cela? S'applique-t-il à une classe qui suit le modèle singleton?

39
Bhaskar

Il n'y a jamais aucune instances de classes statiques: elles sont à la fois abstraites et scellées dans l'IL, donc le CLR empêchera la création de toute instance. Il n'y a donc rien à sérialiser. Les champs statiques ne sont jamais sérialisés, et c'est le seul type d'état qu'une classe statique puisse avoir.

Votre question sur la sérialisation XML n'a aucun sens, car personne ne peut avoir créé une instance de la classe statique pour commencer.

55
Jon Skeet

Vous ne pouvez pas sérialiser des classes static (ou n'importe quelle classe) à l'aide des fonctionnalités de sérialisation .NET intégrées. Vous ne pouvez sérialiser que instances des classes.

17
Mehrdad Afshari

Vous pouvez créer la classe suivante:

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;
            }
        }
    }
}

Vous devez définir une référence à System.Runtime.Serialization.Formatters.Soap.

Dites, dans votre programme, vous souhaitez enregistrer la classe statique suivante:

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

Vous pouvez utiliser le code suivant:

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

Si vous souhaitez charger les données enregistrées (dans le même programme ou dans un autre programme), utilisez le code suivant:

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

Les champs A.s, A.i, A.z obtiendront les nouvelles valeurs chargées.

15
Mikhail Semenov

Pourquoi ne pas simplement utiliser une classe d'instance temporaire qui est un miroir de la classe statique?

[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;
    }
}

Je sais que c'est un peu un hack, mais il atteint le même objectif, tout en autorisant Refactor avant l'exécution et la validation de la valeur pendant l'exécution.

8
SilverX

J'ai trouvé cette réponse vraiment utile pour ma classe de réglage! 1000 merci à vous!

Mais j'ai dû faire quelques changements pour le faire fonctionner, en raison d'un objet non sérialisable et passer à BinaryFormatter en raison de la compatibilité du servicepack

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;
        }
    }
}
0
andi

Une autre solution, mais qui lit et écrit en XML. Vous pouvez également utiliser l'attribut [Non sérialisé] au-dessus d'un champ pour l'empêcher d'être sérialisé.

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;
    }
}
0
headkaze