Am o listă generică de obiecte în C#, și vrea să-l cloneze pe lista. Elementele în listă sunt cloneable, dar nu't pare a fi o opțiune de a face lista.Clone()`.
Există o modalitate ușoară în jurul valorii de acest lucru?
Dacă elementele sunt de tipuri de valoare, atunci puteți face:
List<YourType> newList = new List<YourType>(oldList);
Cu toate acestea, dacă acestea sunt tipurile de referință și doriți o copie profundă (presupunând că elementele pună în aplicare în mod corespunzător ICloneable
), ai putea face ceva de genul asta:
List<ICloneable> oldList = new List<ICloneable>();
List<ICloneable> newList = new List<ICloneable>(oldList.Count);
oldList.ForEach((item) =>
{
newList.Add((ICloneable)item.Clone());
});
Evident, înlocuiți ICloneable
în cele de mai sus generice și a aruncat cu orice tip de element este faptul că pune în aplicare ICloneable
.
Dacă tipul de element nu't suport ICloneable
dar are o copie-constructor, ai putea face acest lucru in schimb:
List<YourType> oldList = new List<YourType>();
List<YourType> newList = new List<YourType>(oldList.Count);
oldList.ForEach((item)=>
{
newList.Add(new YourType(item));
});
Personal, aș evita ICloneabledin cauza necesității de a garanta o copie profundă a tuturor membrilor. În schimb, am'd sugera copy-constructor sau o fabrică metoda place genul tau.CopyFrom(genul tau itemToCopy)
care returnează un nou exemplu de genul tau`.
Oricare dintre aceste opțiuni ar putea fi înfășurat de o metoda (extinderea sau altfel).
Pentru o copie superficială, puteți folosi în loc de GetRange metoda de Listă generică de clasă.
List<int> oldList = new List<int>( );
// Populate oldList...
List<int> newList = oldList.GetRange(0, oldList.Count);
Citat din: Generice Rețete
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;
}
Aceasta este o modalitate de a face cu C# și .NET 2.0. Obiect necesită să fie [Serializable()]
. Scopul este de a pierde toate trimiterile și de a construi altele noi.
După o ușoară modificare, puteți, de asemenea, clona:
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;
}
Pentru a clona o listă de apel .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 }
>
Dacă ai nevoie de o reală clona de fiecare obiect în interiorul Listă
List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);
Modificări la myList
cum ar fi de a introduce sau a scoate nu va afecta `cloneOfMyList și vice-versa.
La obiecte reale cele două Liste conțin sunt aceleasi cu toate acestea.
Dacă îți pasă doar de tipuri de valoare...
Și știi de tipul:
List<int> newList = new List<int>(oldList);
Dacă tu nu't știu de tip înainte, ai'll nevoie de un helper funcția:
List<T> Clone<T>(IEnumerable<T> oldList)
{
return newList = new List<T>(oldList);
}
La doar:
List<string> myNewList = Clone(myOldList);
Utilizarea AutoMapper (sau orice cartografiere lib preferați) pentru a clona este simplu și o mulțime de întreținut.
Defini mapping:
Mapper.CreateMap<YourType, YourType>();
Face magie:
YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);
Dacă aveți deja referire Newtonsoft.Json în proiectul dumneavoastră și obiectele sunt serializeable ai putea folosi întotdeauna:
List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))
Poate nu cel mai eficient mod de a face asta, dar dacă nu're face 100s de 1000 de ori poate nici nu vei observa diferenta de viteza.
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;
}
}
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;
}
Am'll fi norocos dacă cineva citește acest lucru... dar, în scopul de a nu returna o listă de tip obiect din Clona mea de metode, am creat o interfață:
public interface IMyCloneable<T>
{
T Clone();
}
Apoi am specificat extensia:
public static List<T> Clone<T>(this List<T> listToClone) where T : IMyCloneable<T>
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
Și aici este o implementare a interfeței în a/V marcarea software-ului. Am vrut să am și eu metoda Clone() întoarce o listă de VidMark (în timp ce ICloneable interfață vrut metoda mea de a returna o listă de obiect):
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();
}
}
Și în cele din urmă, utilizarea de extindere în interiorul unei clase:
private List<VidMark> _VidMarks;
private List<VidMark> _UndoVidMarks;
//Other methods instantiate and fill the lists
private void SetUndoVidMarks()
{
_UndoVidMarks = _VidMarks.Clone();
}
Cineva ca ea? Orice îmbunătățiri?
Ai putea, de asemenea, pur și simplu converti lista pentru a o matrice folosind ToArray
, și apoi clona matrice folosind Matrice.Clona(...)`.
În funcție de nevoile dumneavoastră, metodele incluse în clasa Matrice ar putea satisface nevoile dumneavoastra.
Puteți utiliza metoda de extindere:
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;
}
};
}
Puteți clona toate obiectele cu ajutorul lor valoare de tip membrilor de exemplu, ia în considerare această clasă:
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
}
};
Notă: dacă aveți de a face orice modificare pe copia (sau clona) aceasta nu va afecta obiectul original.
Dacă ai nevoie de o clonate lista cu aceeași capacitate, puteți încerca acest lucru:
public static List<T> Clone<T>(this List<T> oldList)
{
var newList = new List<T>(oldList.Capacity);
newList.AddRange(oldList);
return newList;
}
Prietenul meu Gregor Martinovic și am venit cu această soluție simplă, folosind un JavaScript Serializer. Nu este nevoie să steagul clase Serializabile și în testele noastre folosind Newtonsoft JsonSerializer chiar mai repede decât folosind BinaryFormatter. Cu extensia metode utilizabile pe fiecare obiect.
Standard .NET JavascriptSerializer opțiune:
public static T DeepCopy<T>(this T value)
{
JavaScriptSerializer js = new JavaScriptSerializer();
string json = js.Serialize(value);
return js.Deserialize<T>(json);
}
Mai repede folosind opțiunea Newtonsoft JSON:
public static T DeepCopy<T>(this T value)
{
string json = JsonConvert.SerializeObject(value);
return JsonConvert.DeserializeObject<T>(json);
}