2010-07-20 8 views
6

Ich habe eine vorhandene Klasse zum Serialisieren und Deserialisieren von Objekten zu/von XML. Es ist eine generische Klasse mit einem einzigen Typparameter T, deren einzige Einschränkung where T : IXmlSerializable ist. Ich möchte jedoch weiterhin diese Klasse für Klassen verwenden können, die nicht IXmlSerializable implementieren, aber das [Serializable] Attribut haben. Wie könnte ich das tun?C# generisches Serialisierungsdienstprogramm Klasse

Aus meiner generischen Klasse:

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

Ich fand this discussion aber es gab keine Lösung gegeben, nur, dass ich nicht where T : Serializable tun können. Der Versuch, where T : SerializableAttribute zu tun, lässt Visual Studio sagen "Kann die versiegelte Klasse 'System.SerializableAttribute' nicht als Typ-Parameter-Einschränkung verwenden".

Edit: basierend auf Stephen's answer, entfernte ich die Einschränkungen für XmlSerializationUtils<T> und hinzugefügt, um diese statische Konstruktor:

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

Antwort

6

Sie kein Attribut als Teil von Generika erfordern kann. Sie können jedoch einen statischen Konstruktor bereitstellen, der nach dem Konstruktor sucht und es auslöst, wenn er nicht gefunden wird.

5

Ich würde nur die Art Zwang beseitigen und die SerializationException fangen, wenn der Typ nicht ordnungsgemäß serialisiert oder deserialisiert ... In der Tat, dies ermöglicht Ihre allgemeine Serialize und Methoden Deserialize

public enum Formatter { Binary, Xml } 

ein Formatter zu akzeptieren das könnte steuern, ob die Serialisierung binär oder XML ist

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

Das ist keine unangemessene Lösung. –

+0

Ja, ich stimme zu, es wird von einem Entwickler verwendet, der weiß, ob die Klasse, die er zu serialisieren versucht, seriell ist, wenn er sie falsch verwendet, für die Ausnahmen vorgesehen sind, können Sie nicht jeden möglichen Fehler zur Kompilierzeit beseitigen. –

+0

@Ben: Wir können das nicht immer tun, aber wir sollten auf jeden Fall versuchen, Fehler früh und oft zu finden. In diesem Fall können wir es zur Kompilierzeit nicht abfangen, aber wenn wir den Trick des statischen Konstruktors verwenden, können wir ihn zu Beginn der Laufzeit abfangen (was bedeutet, dass eine Prüfung nach der Kompilierung nicht fehlschlägt). –

8

Sie können prüfen, ob ein Typ serializable ist die IsSerializable Eigenschaft des Typs des Objekts.

myObj.GetType().IsSerializable 

Wie bereits erwähnt, ist es nicht möglich, diese als generische Integritätsbedingung hinzuzufügen, sie wird jedoch höchstwahrscheinlich in einem Konstruktor überprüft.

+0

Das ist besser als die Suche nach dem Attribut. Vielen Dank. –

+0

Hm. Meine aktuelle Lösung (in meiner Frage) überprüft, ob 'IXmlSerializable' oder' [Serializable] 'auf die gegebene Klasse' T' zutrifft. Ist "IsSerializable" für beides verantwortlich? –

+0

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