2010-04-30 12 views
8

Ich habe verschiedene ObservableCollections von verschiedenen Objekttypen. Ich würde gerne eine einzelne Methode schreiben, die eine Sammlung dieser Objekttypen erstellt und eine neue Sammlung zurückgibt, bei der jedes Element eine tiefe Kopie der Elemente in der angegebenen Sammlung ist. Hier ist ein Beispiel für eine specifc KlasseGenerische Methode zum Erstellen einer tiefen Kopie aller Elemente in einer Sammlung

private static ObservableCollection<PropertyValueRow> DeepCopy(ObservableCollection<PropertyValueRow> list) 
    { 
     ObservableCollection<PropertyValueRow> newList = new ObservableCollection<PropertyValueRow>(); 
     foreach (PropertyValueRow rec in list) 
     { 
      newList.Add((PropertyValueRow)rec.Clone()); 
     } 
     return newList; 
    } 

Wie kann ich diese Methode generisch für jede Klasse machen die ICloneable implementiert?

+4

Als eine gerechte Warnung, sind nicht alle ICloneable-Implementierungen tatsächlich tiefe Kopien. –

Antwort

24

Man könnte so etwas tun:

private static ObservableCollection<T> DeepCopy<T>(ObservableCollection<T> list) 
    where T : ICloneable 
{ 
    ObservableCollection<T> newList = new ObservableCollection<T>(); 
    foreach (T rec in list) 
    { 
     newList.Add((T)rec.Clone()); 
    } 
    return newList; 
} 

Beachten Sie, dass diese allgemeinere machen könnte IEnumerable<T> indem sie, und LINQ macht es noch einfacher:

private static ObservableCollection<T> DeepCopy<T>(IEnumerable<T> list) 
    where T : ICloneable 
{ 
    return new ObservableCollection<T>(list.Select(x => x.Clone()).Cast<T>()); 
} 
+1

Ich hatte das versucht, aber es scheint nicht, dass eine generische Methode. Ich bekomme einen Compilerfehler "Constraints sind nicht bei nicht-generischen Deklarationen erlaubt". – bwarner

+1

Ups - ich hatte einen Tippfehler; Sie brauchen nur '' am Ende des Methodennamens. –

+1

nachdem ich all diese Chuck Norris-Witze über Jon Skeet gelesen habe, finde ich das sehr schwer zu glauben - dass Jon Skeet einen Tippfehler in seinem Posten hatte. Aber eine schöne Post in der Tat. Danke für die nette zweite LINQ-basierte Version. Ordentlich. Sehr gepflegt. –

3
private static ObservableCollection<T> DeepCopy<T>(ObservableCollection<T> list) 
    where T : ICloneable 
{ 
    ObservableCollection<T> newList = new ObservableCollection<T>(); 
    foreach (T rec in list) 
    { 
     newList.Add((T)rec.Clone()); 
    } 
    return newList; 
} 
+2

das scheint "Cloned" Version von Jon Skeets Beitrag zu sein :-) –

+8

Wie denkst du, habe ich einen Code in der richtigen Version um 13:26 geschnitzt, als er es um 14:15 korrigierte ... ich don Ich habe keine Zeitmaschine. – Hinek

+0

+1 für die Zeitmaschine :) – WiiMaxx

0

Ich benutze ein sehr ähnliche Funktion, die mit allen ICollections funktioniert, die konstruiert werden können (zB viele Standardsammlungen):

public static TContainer CloneDeep<TContainer, T>(TContainer r) 
     where T : ICloneable 
     where TContainer: ICollection<T>, new() 
    { 
     // could use linq here, but this is my original pedestrian code ;-) 
     TContainer l = new TContainer(); 
     foreach(var t in r) 
     { 
      l.Add((T)t.Clone()); 
     } 

     return l; 
    } 

Leider kann der Compiler die Typen nicht ableiten, so dass man sie explizit übergeben muss. Für mehr als eine Handvoll Anrufe schreibe ich eine Spezialisierung. Hier ist ein Beispiel für Listen (die selbst mit implizit abgeleitetem T aufgerufen werden können).

public static List<T> CloneListDeep<T>(List<T> r) where T : ICloneable 
    { 
     return CloneDeep<List<T>, T>(r); 
    } 

Ich benutze diese Funktion ausgiebig, um Kopien der Listen als Datenquellen für datagridviews auf Dialoge dienen zu schaffen, die rückgängig gemacht werden kann. Die geänderte Liste wird einfach verworfen, wenn der Dialog abgebrochen wird. Wenn der Dialog OK ist, ersetzt die bearbeitete Liste einfach das Original. Voraussetzung für dieses Muster ist natürlich eine semantisch korrekte und gut gepflegte T.clone().

Verwandte Themen