Kaip klonuoti bendrą sąrašą C #?

Turiu bendrą C # sąrašą objektų sąraše ir noriu klonuoti sąrašą. Sąraše esantys elementai yra klonuojami, tačiau atrodo, kad pasirinkimo nėra.

Ar tai lengva?

492
21 окт. „Fiona “ spalio 21 d. 2008-10-21 19:47 '08 at 7:47 pm 2008-10-21 19:47
@ 25 atsakymai

Galite naudoti išplėtimo metodą.

 static class Extensions { public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable { return listToClone.Select(item => (T)item.Clone()).ToList(); } } 
329
21 окт. atsakymas pateikiamas ajm 21 oct. 2008-10-21 19:58 '08 at 7:58 pm 2008-10-21 19:58

Jei jūsų elementai yra vertės tipai, galite tiesiog:

 List<YourType> newList = new List<YourType>(oldList); 

Tačiau, jei jie yra orientaciniai tipai ir jums reikia gilios kopijos (darant prielaidą, kad jūsų elementai teisingai įgyvendinami), galite tai padaryti:

 List<ICloneable> oldList = new List<ICloneable>(); List<ICloneable> newList = new List<ICloneable>(oldList.Count); oldList.ForEach((item) => { newList.Add((ICloneable)item.Clone()); }); 

Akivaizdu, kad pakeiskite ICloneable pirmiau nurodytomis ICloneable ir taikykite bet kokio tipo elementą, kuris įgyvendina ICloneable .

Jei jūsų elemento tipas nepalaiko „ ICloneable , bet turi kopijavimo konstruktorių, galite tai padaryti:

 List<YourType> oldList = new List<YourType>(); List<YourType> newList = new List<YourType>(oldList.Count); oldList.ForEach((item)=> { newList.Add(new YourType(item)); }); 

Asmeniškai norėčiau išvengti ICloneable nes reikia užtikrinti gilų visų dalyvių kopiją. Vietoj to, aš siūlau kopijavimo konstruktorių arba gamyklos metodą, pvz., YourType.CopyFrom(YourType itemToCopy) , kuri grąžina naują „ YourType .

Bet kuris iš šių parametrų gali būti suvyniotas metodu (pratęsimas arba kitaip).

434
21 окт. Jeff Yates atsakymas, pateiktas spalio 21 d 2008-10-21 19:54 '08, 19:54, 2008-10-21 19:54
 public static object DeepClone(object obj) { object objResult = null; using (MemoryStream ms = new MemoryStream()) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(ms, obj); ms.Position = 0; objResult = bf.Deserialize(ms); } return objResult; } 

Tai yra vienas iš būdų tai padaryti naudojant C # ir .NET 2.0. Jūsų objektas turi būti [Serializable()] . Tikslas - prarasti visas nuorodas ir kurti naujas.

76
21 окт. Patrick Desjardins atsakymas spalio 21 d 2008-10-21 20:43 '08 8:43 pm 2008-10-21 20:43

Dėl seklių kopijų galite naudoti „GetRange“ metodą, esantį bendrų sąrašų klasėje.

 List<int> oldList = new List<int>( ); // Populate oldList... List<int> newList = oldList.GetRange(0, oldList.Count); 

Cituojamas iš: Bendrųjų receptų

69
21 окт. Anthony Potts atsakymas, pateiktas spalio 21 d 2008-10-21 19:52 '08 at 7:52 pm 2008-10-21 19:52

Po nedidelio pakeitimo taip pat galite klonuoti:

 public static T DeepClone<T>(T obj) { T objResult; using (MemoryStream ms = new MemoryStream()) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(ms, obj); ms.Position = 0; objResult = (T)bf.Deserialize(ms); } return objResult; } 
20
20 июля '11 в 12:26 2011-07-20 12:26 atsakymą pateikė Ajith liepos 20 d. 11 val. 12:26 2011-07-20 12:26

Jei List<T> nereikia jokio objekto klono, geriausias būdas klonuoti sąrašą yra sukurti naują sąrašą su senuoju sąrašu kaip rinkimo parametrą.

 List<T> myList = ...; List<T> cloneOfMyList = new List<T>(myList); 

myList pakeitimai, pvz., Įterpimas ar ištrynimas, neturės įtakos „ cloneOfMyList ir atvirkščiai.

Faktiniai objektai, esantys dviejuose sąrašuose, vis dar yra tokie patys.

15
10 июля '15 в 17:09 2015-07-10 17:09 Atsakymą pateikė Jader Feijo liepos 10 d. 15 val. 17:09 2015-07-10 17:09

Jei norite klonuoti sąrašą, tiesiog skambinkite .ToList ()

 Microsoft (R) Roslyn C# Compiler version 2.3.2.62116 Loading context from 'CSharpInteractive.rsp'. Type "#help" for more information. > var x = new List<int>() { 3, 4 }; > var y = x.ToList(); > x.Add(5) > x List<int>(3) { 3, 4, 5 } > y List<int>(2) { 3, 4 } > 
14
25 сент. Atsakymas duotas Xavier John 25 Sep. 2017-09-25 03:35 '17 bent 3:35 2017-09-25 03:35

Naudojant „AutoMapper“ (arba bet kurią kitą pageidaujamą biblioteką) klonavimui yra paprasta ir daug palaikoma.

Nustatykite savo žemėlapį:

 Mapper.CreateMap<YourType, YourType>(); 

Padarykite magiją:

 YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>); 
13
14 февр. Atsakymą pateikė Derek Liang , vasario 14 d. 2013-02-14 02:20 '13, 2:20, 2013-02-14 02:20

Jei jums reikia tik vertės tipų ...

Ir žinote tipą:

 List<int> newList = new List<int>(oldList); 

Jei anksčiau nežinote tipo, jums reikės papildomos funkcijos:

 List<T> Clone<T>(IEnumerable<T> oldList) { return newList = new List<T>(oldList); } 

Tiesiog:

 List<string> myNewList = Clone(myOldList); 
12
21 окт. James Curran atsakymas spalio 21 d 2008-10-21 19:54 '08, 19:54, 2008-10-21 19:54

Jei jūsų projekte jau nurodėte „Newtonsoft.Json“ ir jūsų objektai yra serializuojami, visada galite naudoti:

 List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy)) 

Galbūt tai nėra pats efektyviausias būdas tai padaryti, bet jei tai padarysite 100 kartų per 1000 kartų, greičiausiai net nepastebėsite skirtumo.

8
01 нояб. atsakymą pateikė ProfNimrod 01 nov. 2013-11-01 17:43 '13, 17:43, 2013-11-01 17:43
  public List<TEntity> Clone<TEntity>(List<TEntity> o1List) where TEntity : class , new() { List<TEntity> retList = new List<TEntity>(); try { Type sourceType = typeof(TEntity); foreach(var o1 in o1List) { TEntity o2 = new TEntity(); foreach (PropertyInfo propInfo in (sourceType.GetProperties())) { var val = propInfo.GetValue(o1, null); propInfo.SetValue(o2, val); } retList.Add(o2); } return retList; } catch { return retList; } } 
3
10 апр. atsakymas pateikiamas shahrooz.bazrafshan 10 d. 2016-04-10 10:40 '16 at 10:40 2016-04-10 10:40
 public static Object CloneType(Object objtype) { Object lstfinal = new Object(); using (MemoryStream memStream = new MemoryStream()) { BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone)); binaryFormatter.Serialize(memStream, objtype); memStream.Seek(0, SeekOrigin.Begin); lstfinal = binaryFormatter.Deserialize(memStream); } return lstfinal; } 
3
25 апр. Atsakymas pateikiamas pratik 25 Bal 2011-04-25 15:18 '11, 15:18, 2011-04-25 15:18
 public class CloneableList<T> : List<T>, ICloneable where T : ICloneable { public object Clone() { var clone = new List<T>(); ForEach(item => clone.Add((T)item.Clone())); return clone; } } 
3
07 окт. atsakymą pateikė Peter 07 Oct 2011-10-07 10:04 '11, 10:04, 2011-10-07 10:04

Mano draugas Gregor Martinovich ir aš atėjau šį paprastą sprendimą naudojant „JavaScript“ serializer. Nereikia pažymėti klasių kaip „Serializable“ ir mūsų bandymuose naudojant „Newtonsoft JsonSerializer“, net greičiau nei naudojant „BinaryFormatter“. Su pratęsimo metodais, kuriuos galima naudoti kiekvienam objektui.

Standartinė parinktis .NET JavascriptSerializer:

 public static T DeepCopy<T>(this T value) { JavaScriptSerializer js = new JavaScriptSerializer(); string json = js.Serialize(value); return js.Deserialize<T>(json); } 

Greitesnis variantas naudojant „ Newtonsoft JSON“ :

 public static T DeepCopy<T>(this T value) { string json = JsonConvert.SerializeObject(value); return JsonConvert.DeserializeObject<T>(json); } 
2
09 нояб. Atsakymas pateikiamas FH 09.11. 2016-11-09 16:55 '16 at 16:55 pm 2016-11-09 16:55

Galite naudoti plėtinio metodą:

 namespace extension { public class ext { public static List<double> clone(this List<double> t) { List<double> kop = new List<double>(); int x; for (x = 0; x < t.Count; x++) { kop.Add(t[x]); } return kop; } }; } 

Visus objektus galite klonuoti, pvz., Naudodami jos vertės tipo narius, apsvarstyti šią klasę:

 public class matrix { public List<List<double>> mat; public int rows,cols; public matrix clone() { // create new object matrix copy = new matrix(); // firstly I can directly copy rows and cols because they are value types copy.rows = this.rows; copy.cols = this.cols; // but now I can no t directly copy mat because it is not value type so int x; // I assume I have clone method for List<double> for(x=0;x<this.mat.count;x++) { copy.mat.Add(this.mat[x].clone()); } // then mat is cloned return copy; // and copy of original is returned } }; 

Pastaba: jei atliekate kopijos (arba klonavimo) pakeitimus, jis neturės įtakos pradiniam objektui.

2
07 июня '13 в 14:37 2013-06-07 14:37 atsakymą pateikė vartotojo2463322 birželio 07 d. 13:37 2013-06-07 14:37

Taip pat galite tiesiog konvertuoti sąrašą į masyvą su „ ToArray ir tada klonuoti masyvą su „ Array.Clone(...) . Priklausomai nuo jūsų poreikių, Array klasės metodai gali patenkinti jūsų poreikius.

2
15 янв. Atsakymą pateikė JHaps Jan 15 2015-01-15 18:08 '15, 18:08 2015-01-15 18:08

Jei jums reikia klonuoto sąrašo, kurio talpa yra tokia pati, galite išbandyti šiuos veiksmus:

 public static List<T> Clone<T>(this List<T> oldList) { var newList = new List<T>(oldList.Capacity); newList.AddRange(oldList); return newList; } 
2
22 дек. Atsakymą pateikė vartotojo3245269 gruodžio 22 d. 2015-12-22 05:19 '15, 5:19, 2015-12-22 05:19

Aš padariau savo pratęsimą, kuris konvertuoja ICollection elementus, kurie neįgyvendina IClonable

 static class CollectionExtensions { public static ICollection<T> Clone<T>(this ICollection<T> listToClone) { var array = new T[listToClone.Count]; listToClone.CopyTo(array,0); return array.ToList(); } } 
1
03 июля '13 в 15:41 2013-07-03 15:41 atsakymas pateikiamas wudzik liepos 03 d. 13:41 2013-07-03 15:41

Aš naudoju automapper, jei norite nukopijuoti objektą. Aš tiesiog nustatiau kartografavimą, kuris parodo sau vieną objektą. Šią operaciją galite atlikti bet kokiu būdu.

http://automapper.codeplex.com/

1
13 окт. atsakymas duotas Dan H 13 sp 2014-10-13 17:28 '14, 17:28 2014-10-13 17:28

Man pasisekė, jei kas nors tai perskaito ... bet, kad nenurodytų savo klonų metodų objektų tipų sąrašo, sukūriau sąsają:

 public interface IMyCloneable<T> { T Clone(); } 

Tada aš nurodiau plėtinį:

 public static List<T> Clone<T>(this List<T> listToClone) where T : IMyCloneable<T> { return listToClone.Select(item => (T)item.Clone()).ToList(); } 

Ir čia yra sąsajos diegimas mano A / V ženklinimo programinėje įrangoje. Norėjau, kad „Clone“ () metodas grąžintų „VidMark“ sąrašą (nors „ICloneable“ sąsaja norėjo, kad mano metodas grąžintų objektų sąrašą):

 public class VidMark : IMyCloneable<VidMark> { public long Beg { get; set; } public long End { get; set; } public string Desc { get; set; } public int Rank { get; set; } = 0; public VidMark Clone() { return (VidMark)this.MemberwiseClone(); } } 

Galiausiai, pratęsimų naudojimas klasėje:

 private List<VidMark> _VidMarks; private List<VidMark> _UndoVidMarks; //Other methods instantiate and fill the lists private void SetUndoVidMarks() { _UndoVidMarks = _VidMarks.Clone(); } 

Kiekvienas tai myli? Bet kokie patobulinimai?

0
21 янв. John Kurtz atsakymas Jan 21 2019-01-21 20:15 '19 , 8:15 pm 2019-01-21 20:15

Yra paprastas būdas klonuoti objektus C #, naudojant JSON serializatorių ir deserializer.

Galite sukurti plėtinio klasę:

 using Newtonsoft.Json; static class typeExtensions { [Extension()] public static T jsonCloneObject<T>(T source) { string json = JsonConvert.SerializeObject(source); return JsonConvert.DeserializeObject<T>(json); } } 

Klonas ir objektas:

 obj clonedObj = originalObj.jsonCloneObject; 
0
20 янв. Atsakyti Albert arnau sausio 20 2017-01-20 11:43 '17 at 11:43 2017-01-20 11:43
  //try this List<string> ListCopy= new List<string>(OldList); //or try List<T> ListCopy=OldList.ToList(); 
0
18 февр. Atsakė Steve vasario 18 d 2018-02-18 07:52 '18 at 7:52 am 2018-02-18 07:52

Kitas dalykas: galite naudoti apmąstymus. Jei talpinate jį teisingai, jis klonuos 1.000.000 objektų per 5,6 sekundes (deja, 16,4 sekundės su vidiniais objektais).

 [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)] public class Person { ... Job JobDescription ... } [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)] public class Job {... } private static readonly Type stringType = typeof (string); public static class CopyFactory { static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>(); private static readonly MethodInfo CreateCopyReflectionMethod; static CopyFactory() { CreateCopyReflectionMethod = typeof(CopyFactory).GetMethod("CreateCopyReflection", BindingFlags.Static | BindingFlags.Public); } public static T CreateCopyReflection<T>(T source) where T : new() { var copyInstance = new T(); var sourceType = typeof(T); PropertyInfo[] propList; if (ProperyList.ContainsKey(sourceType)) propList = ProperyList[sourceType]; else { propList = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance); ProperyList.Add(sourceType, propList); } foreach (var prop in propList) { var value = prop.GetValue(source, null); prop.SetValue(copyInstance, value != null  prop.PropertyType.IsClass  prop.PropertyType != stringType ? CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { value }) : value, null); } return copyInstance; } 

Aš jį matavau paprastu būdu naudojant „Watcher“ klasę.

  var person = new Person { ... }; for (var i = 0; i < 1000000; i++) { personList.Add(person); } var watcher = new Stopwatch(); watcher.Start(); var copylist = personList.Select(CopyFactory.CreateCopyReflection).ToList(); watcher.Stop(); var elapsed = watcher.Elapsed; 

REZULTATAS: Su vidiniu PersonInstance objektu - 16.4, PersonInstance = null - 5.6

CopyFactory yra tik mano bandymų klasė, kurioje turiu tuziną testų, įskaitant išraiškos naudojimą. Tai galite padaryti kitoje formoje kaip plėtinys arba kažkas kita. Nepamirškite apie talpyklą.

Dar neišbandžiau serializacijos, bet abejoju, ar pagerėjo milijonai klasių. Bandysiu ką nors greitai protobuf / novoton.

PS: Kad būtų lengviau skaityti, aš naudoju tik automatinę nuosavybę. Galėčiau atnaujinti naudojant „FieldInfo“, arba jūs turėtumėte jį lengvai įgyvendinti.

Neseniai aš išbandiau protokolo buferius serializeriu su DeepClone funkcija iš dėžutės. Jis laimėjo 4,2 sekundės vienam milijonui paprastų objektų, bet kai kalbama apie vidinius objektus, jis laimi 7,4 sekundės rezultatu.

 Serializer.DeepClone(personList); 

APRAŠYMAS: Jei neturite prieigos prie klasių, tai padės. Priešingu atveju tai priklauso nuo objektų skaičiaus. Manau, kad galite naudoti atspindį iki 10 000 objektų (galbūt šiek tiek mažiau), bet daugiau nei tai, kad buferinio protokolo serializatorius veiks geriau.

0
19 дек. Romų Borodovo atsakymą pateikė gruodžio 19 d. 2015-12-19 02:56 '15 at 2:56 2015-12-19 02:56
  public class DemoListClone { private class YearsDto : ICloneable { public object Clone() { return new YearsDto { Year = this.Year }; } public int Year { get; set; } } private List<YearsDto> Org_Years = new List<YearsDto> { new YearsDto { Year = 1966 }, new YearsDto { Year = 1969 } , new YearsDto { Year = 1973 } , new YearsDto { Year = 1985 } , new YearsDto { Year = 2000 } }; private List<YearsDto> Work_Years; public void CloneList() { this.Work_Years = this.Org_Years.Select(x => (YearsDto)x.Clone()).Where(x => x.Year > 1970).ToList(); } } 

pradinis sąrašas = 1966, 1969, 1973, 1985, 2000

darbo sąrašas = 1973, 1985, 2000

0
10 дек. atsakymas pateikiamas Ángel Ibáñez 10 d. 2018-12-10 19:11 '18 19:11 2018-12-10 19:11

Šį kodą reikia perkelti į sąrašą su minimaliais pakeitimais.

Tai iš esmės veikia įterpiant naują atsitiktinį skaičių iš didesnio diapazono su kiekvienu paskesniu ciklu. Jei yra numerių, kurie jau atitinka arba aukštesni, perkelkite šiuos atsitiktinius skaičius į vieną, kad jie pereitų į naują, platesnį atsitiktinių rodiklių diapazoną.

 // Example Usage int[] indexes = getRandomUniqueIndexArray(selectFrom.Length, toSet.Length); for(int i = 0; i < toSet.Length; i++) toSet[i] = selectFrom[indexes[i]]; private int[] getRandomUniqueIndexArray(int length, int count) { if(count > length || count < 1 || length < 1) return new int[0]; int[] toReturn = new int[count]; if(count == length) { for(int i = 0; i < toReturn.Length; i++) toReturn[i] = i; return toReturn; } Random r = new Random(); int startPos = count - 1; for(int i = startPos; i >= 0; i--) { int index = r.Next(length - i); for(int j = startPos; j > i; j--) if(toReturn[j] >= index) toReturn[j]++; toReturn[i] = index; } return toReturn; } 
0
03 сент. atsakymą pateikė Adam Lewis 03 sep. 2015-09-03 14:23 '15, 14:23, 2015-09-03 14:23

Kiti klausimai apie žymes arba Užduoti klausimą