www.rolandk.de
- Aktuelle Themen zu .Net -
Achtung: Hier handelt es sich um meine alte Seite.
Die aktuelle ist unter folgendem Link erreichbar: www.rolandk.de/wp/
Home Blog Die Sache mit dem Klonen




















































Die Sache mit dem Klonen
Freitag, den 03. Juni 2011 um 20:50 Uhr

Das mit dem Klonen unter .Net ist schon so eine Sache. Ich persönlich habe die Schnittstelle IClonable schon öfters verwendet. Ein gutes Beispiel sind Konfigurationsobjekte, die für Oberflächen geklont und erst nach Druck des OK- oder Apply-Buttons übernommen werden.

In der Regel sieht eine Implementierung der Clone-Methode so aus:

 

  1. /// <summary>
  2. /// Clones this object.
  3. /// </summary>
  4. public object Clone()
  5. {
  6. object result = this.MemberwiseClone();
  7.  
  8. //Copy all objects..
  9.  
  10. return result;
  11. }

Sieht auf den ersten Blick recht allgemein verwendbar aus. Die Methode MemberwiseClone erstellt automatisch eine flache Kopie des Objekts, kopiert also alle Member in ein neues Objekt. Aber: Objektreferenzen werden auch einfach nur kopiert. Verweist mal also etwa mithilfe eines Members auf ein Objekt, so zeigt der Klon auf das Gleiche. Sollen solche Objekte auch geklont werden, so muss das manuell programmiert werden. Schlecht ist das vor Allem, wenn zu einem späteren Zeitpunkt Änderungen programmiert werden - die Clone-Methode vergisst man schließlich gerne.

 

Eine kleine Idee, ein solches Problem zu umgehen, ist ein Weg über ein Serialisierungs-Verfahren. Zwar ist dieser Weg bei weitem nicht so Performant, aber dafür sicherer. In meinem Fall sogar viel sicherer, da ich zur Speicherung der Konfiguration den selben Serialisierungs-Algorythmus verwende. Folgendes Coding verwende ich aktuell zum Klonen:

  1. /// <summary>
  2. /// Clones the given object using xml serialization.
  3. /// </summary>
  4. /// <typeparam name="T">The type of the object.</typeparam>
  5. /// <param name="toClone">The object to clone.</param>
  6. public static T CloneUsingXmlSerialization<T>(T toClone)
  7. where T : class
  8. {
  9. if (toClone == null) { throw new ArgumentNullException("toClone"); }
  10.  
  11. StringBuilder xmlString = new StringBuilder();
  12. SerializeToString(toClone, xmlString);
  13. T result = DeserializeFromString<T>(xmlString);
  14. if (result == null)
  15. {
  16. throw new CommonLibraryException(
  17. "Unable to clone the object: Deserialization returned null!");
  18. }
  19.  
  20. return result;
  21. }

Die Serialisierungs-Methoden dazu sehen folgendermaßen aus:

  1. /// <summary>
  2. /// Serializes the given object into the given StringBuilder object.
  3. /// </summary>
  4. /// <param name="toSerialize">The object to serialize.</param>
  5. /// <param name="result">The object to write the result in.</param>
  6. public static void SerializeToString(object toSerialize, StringBuilder result)
  7. {
  8. SerializeToString(toSerialize, result, string.Empty);
  9. }
  10.  
  11. /// <summary>
  12. /// Serializes the given object into the given StringBuilder object.
  13. /// </summary>
  14. /// <param name="toSerialize">The object to serialize.</param>
  15. /// <param name="result">The object to write the result in.</param>
  16. /// <param name="defaultNamespace">The default namespace to use.</param>
  17. public static void SerializeToString(object toSerialize, StringBuilder result, string defaultNamespace)
  18. {
  19. if (toSerialize == null) { throw new ArgumentNullException("toSerialize"); }
  20. if (result == null) { throw new ArgumentNullException("result"); }
  21.  
  22. StringWriter stringWriter = null;
  23. try
  24. {
  25. //Create the serializer
  26. XmlSerializer serializer = null;
  27. if (string.IsNullOrEmpty(defaultNamespace))
  28. {
  29. serializer = new XmlSerializer(toSerialize.GetType());
  30. }
  31. else { serializer = new XmlSerializer(toSerialize.GetType(), defaultNamespace); }
  32.  
  33. //Serialize into local memory stream
  34. stringWriter = new StringWriter(result);
  35. serializer.Serialize(stringWriter, toSerialize);
  36. }
  37. finally
  38. {
  39. if (stringWriter != null) { stringWriter.Dispose(); }
  40. }
  41. }
  42.  
  43. /// <summary>
  44. /// Deserializes an object from the StringBuilder object.
  45. /// </summary>
  46. /// <typeparam name="T">Type of the object.</typeparam>
  47. /// <param name="stringBuilder">The source of the deserialization.</param>
  48. public static T DeserializeFromString<T>(StringBuilder stringBuilder)
  49. where T : class
  50. {
  51. return DeserializeFromString<T>(stringBuilder, string.Empty);
  52. }
  53.  
  54. /// <summary>
  55. /// Deserializes an object from the StringBuilder object.
  56. /// </summary>
  57. /// <typeparam name="T">Type of the object.</typeparam>
  58. /// <param name="stringBuilder">The source of the deserialization.</param>
  59. /// <param name="defaultNamespace">Standard namespace for xml-file.</param>
  60. public static T DeserializeFromString<T>(StringBuilder stringBuilder, string defaultNamespace)
  61. where T : class
  62. {
  63. if (stringBuilder == null) { throw new ArgumentNullException("stringBuilder"); }
  64.  
  65. Type genericType = typeof(T);
  66. StringReader stringReader = null;
  67. try
  68. {
  69. //Create the serializer
  70. XmlSerializer serializer = null;
  71. if (string.IsNullOrEmpty(defaultNamespace)) { serializer = new XmlSerializer(genericType); }
  72. else { serializer = new XmlSerializer(genericType, defaultNamespace); }
  73.  
  74. //Open the file for reading
  75. stringReader = new StringReader(stringBuilder.ToString());
  76.  
  77. return serializer.Deserialize(stringReader) as T;
  78. }
  79. finally
  80. {
  81. if (stringReader != null) { stringReader.Close(); }
  82. }
  83. }

Unterm Strich ist die Methode natürlich nicht für extrem komplexe Objekt-Hierarchien geeignet. Für einfache und leicht verschachtelte Objekte ist es aber eine in meinen Augen sehr sinnvolle Vorgehensweise.

 

Kommentar hinzufügen

Ihr Name:
Kommentar: