2009-02-06 12 views
23

dachte ich, dass eine Liste zu klonen würden Sie rufen Sie einfach an:Cloning Liste <T>

List<int> cloneList = new List<int>(originalList); 

Aber ich habe versucht, dass in meinem Code und ich scheinen Effekte zu bekommen, dass die oben bedeuten einfach macht:

cloneList = originalList ... weil Änderungen an cloneList OriginalList zu beeinflussen scheinen.

Also, wie klont man eine Liste?

EDIT:

Ich denke so etwas wie dies zu tun:

public static List<T> Clone<T>(this List<T> originalList) where T : ICloneable 
{ 
    return originalList.ConvertAll(x => (T) x.Clone()); 
} 

EDIT2:

Ich habe die tiefe Kopie Code vorgeschlagen von Binoj Antony und erstellt diese Erweiterung Methode:

public static T DeepCopy<T>(this T original) where T : class 
{ 
    using (MemoryStream memoryStream = new MemoryStream()) 
    { 
     BinaryFormatter binaryFormatter = new BinaryFormatter(); 
     binaryFormatter.Serialize(memoryStream, original); 
     memoryStream.Seek(0, SeekOrigin.Begin); 
     return (T)binaryFormatter.Deserialize(memoryStream); 
    } 
} 

EDIT3:

Nun sagen die Elemente in der Liste sind Strukturen. Was dann ergäbe, wenn ich ?:

List<StructType> cloneList = new List<StructType>(originalList); 

Ich bin ziemlich sicher, dass genannt, als ich eine Liste mit neuen Unikaten gefüllt bekommen würde, richtig?

+0

Auch die Bearbeitung - beachten Sie, dass ICloneable ist: schlecht unterstützt und b: schlecht angegeben (tief vs. seicht) - es ist nicht sehr im täglichen Gebrauch als Ergebnis gesehen. –

+0

Re die Bearbeitung - originalList.ConvertAll (item => (T) item.Clone()) wäre effizienter - es kann die Größe korrekt einstellen, ohne die Liste neu zuordnen zu müssen. –

+0

danke für beide Punkte, sie sind notiert –

Antwort

16

können Sie den Code unten verwenden, um eine tiefe Kopie der Liste oder ein anderes Objekt unterstützt die Serialisierung zu machen:

Auch Sie dies für jede Version von .NET Framework von v 2.0 und höher verwendet werden können, und die ähnliche Technik kann und auch

public static class GenericCopier<T> 
{ 
    public static T DeepCopy(object objectToCopy) 
    { 
     using (MemoryStream memoryStream = new MemoryStream()) 
     { 
      BinaryFormatter binaryFormatter = new BinaryFormatter(); 
      binaryFormatter.Serialize(memoryStream, objectToCopy); 
      memoryStream.Seek(0, SeekOrigin.Begin); 
      return (T) binaryFormatter.Deserialize(memoryStream); 
     } 
    } 
} 

Sie können es in 1.1 verwendet werden angewendet (die Verwendung von Generika zu entfernen) aufrufen, indem

List<int> deepCopiedList = GenericCopier<List<int>>.DeepCopy(originalList); 

Voll Code zu testen, ob t seine Werke:

static void Main(string[] args) 
{ 
    List<int> originalList = new List<int>(5); 
    Random random = new Random(); 
    for(int i = 0; i < 5; i++) 
    { 
     originalList.Add(random.Next(1, 100)); 
     Console.WriteLine("List[{0}] = {1}", i, originalList[i]); 
    } 
    List<int> deepCopiedList = GenericCopier<List<int>>.DeepCopy(originalList); 
    for (int i = 0; i < 5; i++) 
     Console.WriteLine("deepCopiedList[{0}] value is {1}", i, deepCopiedList[i]); 
} 
+2

Danke, das könnte sich als nützlich erweisen –

+1

Muss jede Klasse, die kopiert wird, das Attribut [Serializable] haben? –

+0

Ich habe nur die Antwort mit vollem Code aktualisiert, um Ihr Szenario zu testen, es funktioniert an meinem Ende :) –

18

Dies würde funktionieren ...

List<Foo> cloneList = new List<Foo>(originalList); 

Wenn Sie sagen, „da Änderungen an cloneList scheinen Originalliste zu beeinflussen.“ - meinen Sie Änderungen an der Liste oder Änderungen an den Artikel ...

Hinzufügen/Entfernen/Austauschen Elemente verändert die Liste - wenn wir also tun:

cloneList.Add(anotherItem); 

Sie sollten feststellen, dass cloneList länger ist als originalList. Wenn jedoch der Inhalt Referenz-Typen (Klassen) sind, dann sind beide Listen noch an den gleichen zugrunde liegenden Objekte zeigen - wenn wir also tun:

cloneList[0].SomeObjectProperty = 12345; 

dann wird dies zeigen auch gegen originalList[0].SomeObjectProperty - es gibt nur eine einzige Objekt (zwischen beiden Listen geteilt).

Wenn dies das Problem ist, müssen Sie die Objekte in der Liste klonen - und dann kommen Sie in das ganze tiefe vs flache Problem ... ist das das Problem?

Für eine flache Kopie, können Sie möglicherweise etwas sehr ähnlich wie die Antwort here - einfach mit TTo = TFrom (vielleicht zu einem einzigen T vereinfachen) verwenden.

+0

so die Tatsache, dass es scheint nicht zu arbeiten, bedeutet, dass ich etwas falsch mache, weil die Art, wie Sie erwähnt, ist die Art, wie ich es gerade mache –

+1

Ich antworte in einem Update –

+0

ahhh ... Ich verstehe. Vielen Dank. Ich habe missverstanden, wie flach eine flache Kopie wirklich war :) –

1

Es heißt speziell here, dass Elemente in Ihre neue Liste kopiert werden. Also ja, das sollte funktionieren. Mit Werttypen erhalten Sie völlige Unabhängigkeit.Denken Sie jedoch daran, dass die Listen bei Referenztypen unabhängig sind, aber auf dieselben Objekte verweisen. Sie müssen die Liste gründlich kopieren.

0

      List list = new List(); 
      List clone = new List (list); 
      list.Add (new int()); 
      Debug.Assert (list != clone); 
      Debug.Assert (list.Count == 1); 
      Debug.Assert (clone.Count == 0); 

Dieser Code funktioniert perfekt wie für mich vorgesehen. Ändern Sie vielleicht die Objekte in der Liste? Die Listenpunkte werden nicht geklont von new List(oldList).

+0

das ist was ich gerade gelernt habe, danke :) Ich dachte, dass die Liste Elemente kopieren würde. wir leben und wir lernen. –

+0

Wenn ich IClonable für die Klasse implementieren Liste hält, wird diese Methode sie Klonen die Elemente in der Liste? –

+0

Ich bin mir ziemlich sicher, dass es nicht geht. new List (oldList) erstellt einfach eine neue Liste und kopiert die Referenzen (!) in die Elemente von oldList. – mafu

2

Die Verwendung des Listenkonstruktors mit der ursprünglichen Liste als Parameter funktioniert, wenn der zugrunde liegende Typ der Liste ein Werttyp ist. Als Referenztyp Listenelemente, ich denke, Sie wollen tiefe Kopie ihnen.

Sie so etwas tun könnte:

(Unter der Annahme, dass der zugrunde liegende Typ ICloneable implementiert)

originalList.ForEach((item) => 
         { 
         cloneList.Add((ICloneable)item.Clone()); 
         } 
        ); 

Oder mit einigen LINQ:

var cloneList = originalList.Select(item => (ICloneable)item.Clone()).ToList(); 
+0

schön, jetzt bekommen wir irgendwo –

+1

Zur Klarheit, ICloneable wird weder als tief oder flach angegeben - es ist oft flach. Ebenso sind Struktur-Blits auch flach. Beachten Sie auch, dass ICloneable ziemlich schlecht unterstützt wird. –

8

ich, dass Ihr zweifeln tatsächliche Beispiel hätte Probleme, weil int ist ein Werttyp. Zum Beispiel:

using System; 
using System.Collections.Generic; 

class Test 
{ 
    static void Main() 
    { 
     List<int> originalList = new List<int> { 5, 6, 7 }; 
     List<int> cloneList = new List<int>(originalList); 

     cloneList.Add(8); 
     cloneList[0] = 2; 
     Console.WriteLine(originalList.Count); // Still 3 
     Console.WriteLine(originalList[0]); // Still 5 
    } 
} 

Doch wie Marc sagt, wenn Ihre Liste änderbare Referenztypen enthält, wird nur die Liste Klonen wird eine flache Kopie nehmen - wenn Sie die Objekte mutieren, dass die Listen beziehen, werden diese Änderungen über beide Listen sichtbar sein. Elemente in einer Liste zu ersetzen das äquivalent Element in der anderen Liste aber nicht ändern:

using System; 
using System.Collections.Generic; 

class Dummy 
{ 
    public int Value { get; set; } 

    public Dummy (int value) 
    { 
     this.Value = value; 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
     List<Dummy> originalList = new List<Dummy> 
     { 
      new Dummy(5), 
      new Dummy(6), 
      new Dummy(7) 
     }; 

     List<Dummy> cloneList = new List<Dummy>(originalList); 

     cloneList[0].Value = 1; 
     cloneList[1] = new Dummy(2); 
     Console.WriteLine(originalList[0].Value); // Changed to 1 
     Console.WriteLine(originalList[1].Value); // Still 6 
    } 
} 

einen „tiefen Klon“ eine Liste zu nehmen, wo der Elementtyp ICloneable implementiert, verwenden Sie:

List<Foo> cloneList = originalList.ConvertAll(x => (Foo) x.Clone()); 

Die tatsächliche Tiefe dieses Klons hängt jedoch von der Implementierung von ICloneable in dem Elementtyp ab - ICloneable wird allgemein als eine schlechte Sache betrachtet, weil sein Vertrag so vage ist.

+0

Ich dachte daran, etwas zu tun, was ich gerade in meinen Schnitt zum ursprünglichen Beitrag geschrieben habe. –

2

Ich stimme nicht auf Objekt Serialisierung verlassen. Es ist teuer und schlechte Praxis.

public static TObj CloneObject<TObj>(this TObj obj) 
    where TObj : ICloneable 
{ 
    return (TObj)obj.Clone(); 
} 

Das obige Verfahren ist viel eleganter, und Sie sollten wirklich eine clonable Schnittstelle zu implementieren egal, wenn man einen braucht. Sie könnten es auch generisch machen.

public interface ICloneable<T> : IClonable 
{ 
    T CloneObject(); 
} 

Optional könnten Sie verzichten die IClonable Schnittstelle als Basistyp verwenden, da es schlecht gewartete wird. Der Methodenname muss geändert werden, da Überladungen für Rückgabetypen nicht möglich sind.

public static List<T> CloneList(this List<T> source) 
    where TObj : ICloneable 
{ 
    return source.Select(x=>x.CloneObject()).ToList(); 
} 

Es ist so einfach.

Vielleicht kann Ihr Problem gelöst werden, obwohl stattdessen Werttypen verwendet werden. Sie sind immer Pass-by-Copy. Sie müssen also nie etwas klonen, solange Ihre Datenstruktur wertmäßig ist.

+0

Haben Sie an diesem Code Tests durchgeführt, um zu überprüfen, ob diese Leistung besser ist als die Objektserialisierung? –

+0

Implementierung tiefere Kopie Klonen ohne Serialisierung verwendet, ist fast unmöglich, den ich je nur empfehlen versuchen ICloneable Sie sich, wenn Ihr Objekt ist zu 100% vollständig eine Eigenschaft Tasche von primitiven Typen zu implementieren, dass Sie die MemberwiseClone() -Methode können Sie Ihr Objekt zu kopieren. –

+0

Da Art Objektserialisierung Sie tun ist geistlos Reflexion es langsamste Art von Serialisierung sein wird, wenn Sie eine Klonierungsstrategie implementieren es wird schneller sein, weil es wird nicht auf Reflexion verlassen. Im Gegensatz dazu müssen alle Objekte in Ihrem Objektdiagramm klonbar sein, damit sie funktionieren. –

0

Ich muss hinzufügen: Wenn Sie Serialisierung verwenden, um tiefes Kopieren zu erleichtern, warum würden Sie jedes einzelne Element klonen? Klonen Sie einfach die gesamte ursprüngliche Liste, um mit zu beginnen.

Sofern Sie nicht über Logik verfügen, die nur Knoten klont, die bestimmte Kriterien erfüllen, tun Sie es Knoten für Knoten.