2010-07-20 13 views
6

Tengo una clase existente para serializar y deserializar objetos a/desde XML. Es una clase genérica con un parámetro de tipo único T cuya única restricción es where T : IXmlSerializable. Sin embargo, aún quiero poder utilizar esta clase en las clases que no implementan IXmlSerializable pero tienen el atributo [Serializable]. ¿Cómo podría hacer esto?Clase de utilidad de serialización genérica C#

Desde mi clase genérica:

public static class XmlSerializationUtils<T> where T : IXmlSerializable 
{ 
    public static T DeserializeXml(XmlDocument xml) { ... } 
    public static XmlDocument SerializeToXml(T toSerialize) { ... } 
} 

encontré this discussion pero no había solución dada, sólo que no puedo hacer where T : Serializable. Intentar hacer where T : SerializableAttribute hace que Visual Studio diga "No se puede usar la clase sellada 'System.SerializableAttribute' como parámetro constraint de tipo".

Editar: basado en Stephen's answer, que eliminan las limitaciones en XmlSerializationUtils<T> y ha añadido este constructor estático:

static XmlSerializationUtils() 
{ 
    Type type = typeof(T); 
    bool hasAttribute = null != Attribute.GetCustomAttribute(type, 
     typeof(SerializableAttribute)); 
    bool implementsInterface = 
     null != type.GetInterface(typeof(IXmlSerializable).FullName); 
    if (!hasAttribute && !implementsInterface) 
    { 
     throw new ArgumentException(
      "Cannot use XmlSerializationUtils on class " + type.Name + 
      " because it does not have the Serializable attribute " + 
      " and it does not implement IXmlSerializable" 
     ); 
    } 
} 

Respuesta

6

No se puede requerir un atributo como parte de los genéricos. Sin embargo, podría proporcionar un constructor estático que lo verifique y arroje si no se encuentra.

5

que acababa de eliminar la restricción de tipo y coger el SerializationException cuando el tipo no serializa o deserializar correctamente ... De hecho, esto permite que su Serialize genérico y deserializar métodos para aceptar un formateador

public enum Formatter { Binary, Xml } 

que pudiera controlar si la serialización es binario o XML

public class Serialization 
{ 
    public enum Formatter { Binary, Xml } 

    #region Serialization methods 
    public static void Serialize2File<T>(T obj, string pathSpec, 
     Formatter formatter) 
    { 
     try 
     { 
      switch (formatter) 
      { 
       case (Formatter.Binary): 
        using (var fs = new FileStream(pathSpec, FileMode.Create, 
             FileAccess.Write, FileShare.Write)) 
         (new BinaryFormatter()).Serialize(fs, obj); 
        break; 

       case (Formatter.Xml): 
        var serializer = new XmlSerializer(typeof(T)); 
        TextWriter textWriter = new StreamWriter(pathSpec); 
        serializer.Serialize(textWriter, obj); 
        textWriter.Close(); 
        break; 

       default: 
        throw new MyCustomException("Invalid Formatter option"); 
      } 
     } 
     catch (SerializationException sX) 
     { 
      var errMsg = String.Format(
       "Unable to serialize {0} into file {1}", 
       obj, pathSpec); 
      throw new MyCustomException(errMsg, sX); 
     } 
    } 
    public static T DeSerializeFromFile<T>(string pathSpec, 
     Formatter formatter) where T : class 
    { 
     try 
     { 
      switch (formatter) 
      { 
       case (Formatter.Binary): 
        using (var strm = new FileStream(pathSpec, 
             FileMode.Open, FileAccess.Read)) 
        { 
         IFormatter fmt = new BinaryFormatter(); 
         var o = fmt.Deserialize(strm); 
         if (!(o is T)) 
          throw new ArgumentException("Bad Data File"); 
         return o as T; 
        } 

       case (Formatter.Xml): 
        var serializer = new XmlSerializer(typeof(T)); 
        TextReader rdr = new StreamReader(pathSpec); 
        return (T)serializer.Deserialize(rdr); 

       default: 
        throw new MyCustomException("Invalid Formatter option"); 
      } 
     } 
     catch (SerializationException sX) 
     { 
      var errMsg = String.Format(
       "Unable to deserialize {0} from file {1}", 
       typeof(T), pathSpec); 
      throw new MyCustomException(errMsg, sX); 
     } 
    } 
    #endregion Serialization methods 
} 
+0

No es una solución poco razonable. –

+0

Sí, estoy de acuerdo, está siendo utilizado por un desarrollador que sabe si la clase que intenta serializar es confiable, si la usa mal, para qué sirven las excepciones, no puede eliminar todos los errores posibles en el momento de la compilación. –

+0

@Ben: no siempre podemos hacerlo, pero sin duda debemos tratar de detectar errores de manera temprana y frecuente. En este caso, no podemos detectarlo en tiempo de compilación, pero si usamos el truco del constructor estático, podemos detectarlo al comienzo del tiempo de ejecución (lo que significa que no se perderá una prueba de humo posterior a la compilación). –

8

Usted puede comprobar para ver si un tipo es serializable mediante la propiedad IsSerializable del tipo del objeto.

myObj.GetType().IsSerializable 

Como se mencionó, no se puede agregar como una restricción genérica, pero probablemente se verifique en un constructor.

+0

Esto es mejor que buscar el atributo. Gracias. –

+0

Hm. Mi solución actual (en mi pregunta) comprueba si 'IXmlSerializable' o' [Serializable] 'se aplica a la clase dada' T'. ¿'IsSerializable' cuenta para ambos? –

+0

Aparentemente. Consulte http://msdn.microsoft.com/en-us/library/system.type.isserializable.aspx –

Cuestiones relacionadas