2010-07-20 15 views
6

Ho una classe esistente per serializzare e deserializzare oggetti da/verso XML. È una classe generica con un singolo parametro di tipo T il cui unico vincolo è where T : IXmlSerializable. Tuttavia, voglio essere ancora in grado di utilizzare questa classe su classi che non implementano IXmlSerializable ma hanno l'attributo [Serializable]. Come potrei andare a fare questo?C# classe di utilità di serializzazione generica

Dalla mia classe generica:

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

ho trovato this discussion ma non c'era soluzione data, solo che io non posso fare where T : Serializable. Provare a fare where T : SerializableAttribute rende Visual Studio dire "Impossibile utilizzare la classe sealed System.SerializableAttribute" come tipo di parametro vincolo ".

Edit: sulla base di Stephen's answer, ho rimosso i vincoli XmlSerializationUtils<T> e ha aggiunto questo costruttore statico:

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

risposta

6

Non è possibile richiedere un attributo come parte di farmaci generici. Tuttavia, è possibile fornire un costruttore statico che ne verifica la presenza e genera se non è stato trovato.

5

avevo appena eliminare il vincolo di tipo e prendere il SerializationException quando il tipo non serializzare o deserializzare correttamente ... In realtà, questo permette al Serialize generico e deserializzare metodi di accettare un formattatore

public enum Formatter { Binary, Xml } 

in grado di controllare se la serializzazione è 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

Questa non è una soluzione irragionevole. –

+0

Sì, sono d'accordo, viene usato da uno sviluppatore che sa se la classe che sta cercando di serializzare è serlabile, se la usa male, per quali eccezioni ci sono, non è possibile eliminare ogni bug possibile in fase di compilazione. –

+0

@ Ben: Non possiamo sempre farlo, ma dovremmo sicuramente cercare di catturare i bug in anticipo e spesso. In questo caso, non possiamo prenderlo in fase di compilazione, ma se usiamo il trucco statico del costruttore, possiamo prenderlo all'inizio del tempo di esecuzione (il che significa che un controllo del fumo post-compilazione non mancherà). –

8

è possibile controllare per vedere se un tipo è serializzabile utilizzando la proprietà IsSerializable del tipo dell'oggetto.

myObj.GetType().IsSerializable 

Come accennato, questo non è possibile aggiungere come un vincolo generico, ma sarebbe molto probabilmente controllato in un costruttore.

+0

Questo è meglio che controllare l'attributo. Grazie. –

+0

Hm. La mia soluzione corrente (nella mia domanda) controlla se 'IXmlSerializable' o' [Serializable] 'si applica alla data classe' T'. L'account 'IsSerializable' per entrambi? –

+0

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

Problemi correlati