2010-04-30 15 views
8

Ho varie collette osservabili di diversi tipi di oggetto. Mi piacerebbe scrivere un singolo metodo che prenderà una collezione di questi tipi di oggetti e restituirà una nuova collezione in cui ogni elemento è una copia profonda degli elementi nella collezione data. Ecco un esempio di una classe specifcMetodo generico per creare una copia profonda di tutti gli elementi di una raccolta

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

Come posso fare questo metodo generico per qualsiasi classe che implementa ICloneable?

+4

Come giusto avvertimento, non tutte le implementazioni ICloneable sono in realtà copie profonde. –

risposta

24

Si potrebbe fare qualcosa di simile:

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

Nota che si potrebbe fare questo più generale prendendo IEnumerable<T>, e LINQ rende ancora più facile:

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

L'avevo provato, ma non sembra considerare un metodo generico. Ottengo un errore del compilatore "Vincoli non consentiti su dichiarazioni non generiche". – bwarner

+1

Oops - Ho avuto un errore di battitura; hai solo bisogno di '' alla fine del nome del metodo. –

+1

dopo aver letto tutte queste barzellette di tipo Chuck Norris su Jon Skeet, trovo questo molto difficile da credere - che Jon Skeet avesse un refuso nel suo post. Ma un bel post davvero. Grazie per la nuova versione basata su LINQ. Neat. Molto pulito. –

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

questa sembra la versione "clonata" del post di Jon Skeet :-) –

+8

Come pensi di aver "clonato" un codice tagliato nella versione corretta alle 13:26 quando lo ha corretto alle 14:15 ... I don avere una macchina del tempo – Hinek

+0

+1 per la macchina del tempo :) – WiiMaxx

0

io uso un funzione molto simile che funziona con tutte le ICollections che possono essere costruite (ad es. molte collezioni standard):

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

Sfortunatamente il compilatore non è in grado di dedurre i tipi in modo che sia necessario passarli esplicitamente. Per più di una manciata di chiamate scrivo una specializzazione. Ecco un esempio per Liste (che può essere chiamato da sé con deduzione implicita T).

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

Io uso questa funzione ampiamente al fine di creare copie di elenchi che fungono da origini dati per DataGridViews su finestre di dialogo che possono essere annullati. L'elenco modificato viene semplicemente scartato quando la finestra di dialogo viene cancellata; quando la finestra di dialogo è attiva, la lista modificata sostituisce semplicemente l'originale. Prerequisito per questo modello è, ovviamente, avere uno T.clone() semanticamente corretto e ben mantenuto.

Problemi correlati