2009-08-18 25 views
36

¿Qué ocurre si serializamos una clase estática? ¿Se puede crear más de una instancia de la clase estática si la serializamos?serializar una clase estática?

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

    } 

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

Supongamos que XMLSERIALIZE el objeto a un archivo XML, y en un momento posterior que de-serializar de nuevo a un objeto. Existe otra copia en la memoria (creada cuando alguien instintó el calss estático por primera vez). ¿Habrá dos copias del objeto? Si es así, ¿podemos detener eso? ¿Se aplica a cualquier clase que sigue el patrón singleton?

+0

Estoy pensando que hay algo de confusión aquí entre "singleton" y "estático" ... –

+1

"Supongamos que XmlSerialize el objeto": ¿qué objeto? no hay ningún objeto allí (es decir, no hay instancia), ya que la clase es estática –

Respuesta

52

Nunca hay ninguna instancia de de clases estáticas: ambas son abstractas y están selladas en la IL, por lo que la CLR evitará que se creen instancias. Por lo tanto, no hay nada que serializar. Los campos estáticos nunca se serializan, y ese es el único tipo de estado que una clase estática puede tener.

Su pregunta sobre la serialización XML no tiene sentido, ya que nadie puede haber creado una instancia de la clase estática para empezar.

+1

qué tal esto: 'public static class Constants { public const string Con1 =" con1 "; public const string Con2 = "con2"; } ' Me gustaría pasar esto dinámicamente a javascript, en ese caso, tiene sentido una forma de serialización. Sí, la palabra _serialize_ puede tener significados ligeramente diferentes en la actualidad. –

+0

@orhor: ¿Qué quiere decir con "pasar esto"? Si desea pasar "una colección de constantes", podría hacerlo razonablemente fácilmente, pero no creo que eso cambie el significado normal del término "serializar". –

+0

ok, literalmente: Necesito llamar a un servicio desde javascript y deserializar su respuesta en un objeto javascript relacionado con el tipo de C# del servicio. Por los significados ligeramente diferentes me refiero a esas dos alternativas: 1. traducir un _objeto_ (que implica una instancia) en un formato almacenable 2. traducir cualquier cosa, solo datos de memoria, un tipo o lo que quieras en un formato de texto. Por ejemplo, transferible a través de una red desde el servicio al cliente pero sin ofender, por favor, no estoy insultando su respuesta, simplemente estoy (con suerte) ampliando el espectro de información en esa página. Y gracias por las preguntas. –

17

No puede serializar static clases (o cualquier clase) utilizando funciones de serialización .NET incorporadas. Solo puede serializar instancias de clases.

+0

Pero, ¿puede existir la situación anterior? Es decir, ¿puede haber más de una instancia de una clase estática? – Bhaskar

+1

No. No puede suceder –

12

puede crear la siguiente clase:

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

debe definir una referencia a System.Runtime.Serialization.Formatters.Soap.

Say, en su programa que desea guardar la siguiente clase estática:

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

Usted puede utilizar el siguiente código:

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

Si desea cargar los datos guardados (en el mismo programa o en otro programa), use el siguiente código:

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

Los campos As, Ai, Az will ge t los nuevos valores cargados.

7

¿Por qué no utilizar una clase de instancia temporal que es un espejo de la clase estática?

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

sé que es un poco de un truco, pero acheives el mismo propósito, al tiempo que permite Refactor antes de tiempo de ejecución y validación de valores en tiempo de ejecución.

+1

¿No hay explicación para el voto negativo? Las clases estáticas no se pueden serializar, y esta es una solución aceptable, me pregunto por qué la reflexión es la mejor respuesta en este escenario. Requiere más recursos y es mucho más lento que simplemente copiar los datos a un objeto que PUEDE ser serializado. hmmm – SilverX

+0

La ruta de reflexión es MUCAS menos líneas de código si tienes muchas clases o, de hecho, clases con muchos campos. – LeeCambl

0

¡He encontrado esta respuesta realmente útil para mi clase de configuración! 1000 gracias a ti!

pero tenía que hacer algunos cambios para hacer que funcione, debido a un objeto no serializable y el cambio a BinaryFormatter debido a la compatibilidad 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

Otra solución, pero uno que lee y escribe en XML. También puede usar el atributo [NonSerialized] sobre un campo para evitar que se serialice.

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