Deep Copy von einer List(of)

  • VB.NET

Es gibt 61 Antworten in diesem Thema. Der letzte Beitrag () ist von Gonger96.

    Deep Copy von einer List(of)

    Hi,

    ich habe da noch ein Prob(hoffentlich das letzte für heute). Ich möchte ein Deep Copy von einer List(of double) oder besser genereller List(of Class). Weiß einer wie ich das machen?
    Ich habe nun ein wenig gesucht und finde mehrmals den Hinweiß, dass das über serialisieren geht. Habe aber keine Ahnung wie ich das machen soll.

    Danke schonmal!
    Für alle Datentypen, wie z.B. Double, wäre es einfach Dim newList As New List(Of T)(oldList), da Datentypen bei Neuzuweisung automatisch kopiert werden.
    Für Referenztypen gibt es verschiedene Möglichkeiten. Wenn der Typ eine Clone-Funktion bereitstellt, wäre diese zu verwenden. Ist der Typ serialisierbar, ist das selbstverständlich auch eine Möglichkeit. In seltenen Fällen kann der Typ auch einen passenden Konstruktor o.ä. bereitstellen.
    Eine allgemein gültige Lösung ließe sich nur mit Reflection realisieren, wobei der Typ rekursiv "durchleuchtet" werden muss, bis er auf seine primitiven Datentypen reduziert wurde. Das ist allerdings kompliziert und sehr Performancelastig.
    Am Einfachsten funktioniert das mit dem BinaryFormatter.

    VB.NET-Quellcode

    1. Public Function DeepCopy(Of T)(origin As T) As T
    2. Using InMemory As New MemoryStream
    3. Dim binaryFormatter As New BinaryFormatter()
    4. binaryFormatter.Serialize(InMemory, origin)
    5. InMemory.Seek(0, SeekOrigin.Begin)
    6. Return DirectCast(binaryFormatter.Deserialize(InMemory), T)
    7. End Using
    8. End Function

    Das ist die einzige mir bekannte Möglichkeit, wirklich alles tief zu kopieren. Deine Klassen müssen dann aber mit <Serializable> ausgestattet sein.
    Du kopierst eine Liste von 1000 Elementen 20 mal pro Sekunde? Warum das? Da gibt es sicherlich ne bessere Variante.
    Ansonsten: das braucht alles CPU Leistung und Arbeitsspeicher. Die 1000 Elemente werden 1:1 kopiert, bedeutet der Arbeitsspeicherbedarf verdoppelt sich, sobald du das einmalig ausführst. Wenn du das nun zwanzig mal durchführst, vervierzigfachst du den Arbeitsspeicherbedarf - allerdings nur für die Listen, denn die Serialisierung ansich verwirft die Daten, nachdem die Liste zurückgegeben wurde.
    ich habe einen Datenstrom von ca. 1000 werte pro sekunde. Wo die herkommen ist ja erstmal egal. Und diese müssen halt einmal in einer und in einem array gespeichert werden. Und zwar müssen die unabhängig sein. Also wenn ich das eine verändere dürfen die anderen Werte nicht betroffen sein. Achja ich bekomme die Werte als Array.
    So und das ganze bei 20(vll. eher 15) verschiedenen Datenströmen.

    Eggord schrieb:

    ich habe einen Datenstrom von ca. 1000 werte pro sekunde.
    Wenn ich das so lese, scheint mir ein einfaches Ablegen in einer List(Of T) völlig ausreichend.
    Es sei denn, Dein "Datenstrom" ist etwas eigenes. Kannst Du das bitte mal etwas beleuchten?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    sonne75 schrieb:

    falsch?
    Nö. Aber umständlich.

    Artentus schrieb:

    Dim newList As New List(Of T)(oldList)
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    sonne75 schrieb:

    Referenztypen
    Ich hab mal fix einen einfachen Test gemacht:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    3. Dim TestList1 As New List(Of Test)
    4. For i = 1 To 10
    5. TestList1.Add(New Test(i))
    6. Next
    7. Dim TestList2 As New List(Of Test)(TestList1)
    8. End Sub ' hier ein Haltepunkt drauf setzen und TestList2 ansehen
    9. End Class
    10. Public Class Test
    11. Public Property T1 As Integer
    12. Public Sub New(t As Integer)
    13. T1 = t
    14. End Sub
    15. Public Overrides Function ToString() As String
    16. Return T1.ToString()
    17. End Function
    18. End Class

    Bilder
    • DeepCopy.png

      16,49 kB, 298×323, 172 mal angesehen
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    sonne75 schrieb:

    Heißt es jetzt, dass man damit auch Referenztypkopien machen kann?
    Das muss natürlich ausprobiert werden.
    Ich hab hier nur eine einfache Klasse mit einer Integer-Property gemacht, kompliziertere Klassen könnten da schon echt problematisch sein.
    Allerdings glaube ich, dass @Eggord solch tiefgehendes Zeugs nicht braucht.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Wow hier gehts ja richtig ab während ich noch halb verschlafen rumlaufe. Also irgendwie habe ich die angewohnheit meine situation nicht gut zu beschreiben. Deshalb versuch ich es nochmal:

    Ich habe externe Daten(also z.B. audiodaten oder so) welche mit 5 kHz aufgenommen werden und meinem Program in Paketen mit 2000 Werte alle 0.4 s als Array zur verfügung stehen. Diese Daten werden dann verarbeitet. Also es wird jeder Wert "angefasst" abei werden dann die verarbeitete Daten in ein neues Array gefüllt und an die Klasse zum Darstellen der Daten übergeben. Die Daten sollen aber auch in einer List(of double) gespeichert werden um später in einer CSV datei gespeichert werden. Somit muss ich(denke ich zumindest) von dem verarbeiteten Array eine Deep Copy anlegen, da wenn ich die Daten an die Darstellungsklasse übergebe nach einer Zeit gelöscht werden. Und das wäre ja wenn ch blos die referenzen kopiere schlecht, da dann die Daen in der List auch gelöscht werden würden.
    Nach meiner Methode wird nicht bloß eine Referenz angelegt, sonderen eine richtige Kopie. Ich bin häufig genug darauf reingefallen (und lange nach Fehler gesucht), um es nicht verinnerlicht zu haben.

    EDIT: Ich habe gerade getestet, auch nach der kurzen Methode wird KEINE blosse Referenz erstellt, sondern eine richtige Kopie. Wenn man die Werte in der ersten List verändert, bleiben sie in der zweiten unverändert.
    @RodFromGermany dein Test ist falsch, weil du nichts an den Referenzen änderst.
    Hier eine angepasste Variante:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System
    2. Imports System.Collections.Generic
    3. Public Module Module1
    4. Public Sub Main()
    5. Dim testList1 As New List(Of Test)
    6. For i = 1 To 10
    7. testList1.Add(New Test(i))
    8. Next
    9. Dim testList2 As New List(Of Test)(TestList1)
    10. testList1(0).T1 = 5
    11. Console.WriteLine(string.Join(", ", testList1))
    12. Console.WriteLine(string.Join(", ", testList2))
    13. End Sub
    14. End Module
    15. Public Class Test
    16. Public Property T1 As Integer
    17. Public Sub New(t As Integer)
    18. T1 = t
    19. End Sub
    20. Public Overrides Function ToString() As String
    21. Return T1.ToString()
    22. End Function
    23. End Class


    Gleiches gilt auch für die Variante von @sonne75:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System
    2. Imports System.Collections.Generic
    3. Public Module Module1
    4. Public Sub Main()
    5. Dim testList1 As New List(Of Test)
    6. For i = 1 To 10
    7. testList1.Add(New Test(i))
    8. Next
    9. Dim testList2 As New List(Of Test)
    10. For Each item in testList1
    11. testList2.Add(item)
    12. Next
    13. testList1(0).T1 = 5
    14. Console.WriteLine(string.Join(", ", testList1))
    15. Console.WriteLine(string.Join(", ", testList2))
    16. End Sub
    17. End Module
    18. Public Class Test
    19. Public Property T1 As Integer
    20. Public Sub New(t As Integer)
    21. T1 = t
    22. End Sub
    23. Public Overrides Function ToString() As String
    24. Return T1.ToString()
    25. End Function
    26. End Class

    Ausgabe:

    Quellcode

    1. 5, 2, 3, 4, 5, 6, 7, 8, 9, 10
    2. 5, 2, 3, 4, 5, 6, 7, 8, 9, 10

    Damit ist bewiesen, dass die Listen referenzgleich sind und somit nicht das machen, was gefordert war.

    Wenn du das ganze in einer List(Of Double) speicherst, kannst du doch einfach dein Array mit verarbeiteten Werten nehmen, und via List(Of T).AddRange(T[] data) das Array einfügen. Double ist kein Referenz-, sondern ein Werttyp, wodurch das ganze Problem mit den Referenzen garnicht erst auftritt.

    RodFromGermany schrieb:

    Ich hab mal fix einen einfachen Test gemacht:


    Und jetzt änderst du die Eigenschaft T1 eines Elements aus der ersten Liste und schaust dir danach die zweite Liste an. Ohne das ist der Test unvollständig in Bezug auf die Problemstellung.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Interessant, ich habe erst:

    VB.NET-Quellcode

    1. TestList1(5)=New Test(25)


    gemacht, da hat sich TestList2 NICHT verändert. Aber bei

    VB.NET-Quellcode

    1. TestList1(5).T1=25


    war sie verändert. Was ist denn der Unterschied?

    EDIT: interessanterweise wird nach meiner Methode so auch verändert, dabei funktioniert es bei mir sonst in Projekten ?(
    ah ok. Dass heißt wenn man Wertetypen mit Add in eine List hinzufügt wird eine Deepcopy erstellt. Das ist schonmal gut zuwissen!

    Wie sieht aber das ganze aus wenn es sich um eine eigene Klasse handelt? Dann sollte das eigentlich(nach meiner recherche) nicht funktionieren.

    @RodFromGermany: gibt es so eine kurze schreibweise auch für ein Array welches man zu einer List machen möchte?
    Nein, wenn du einen Werttyp mit .Add hinzufügst, wird der WERT eingefügt. Da gibt es keine DeepCopy, weil der Wert IMMER kopiert wird.
    Wie gesagt: ein Objekt (Klasse) bleibt solange am Leben, wie irgendwo noch eine Referenz darauf besteht.
    Array zu einer Liste machen: Array.ToList() bzw. New List(Of T)(data As T()).