Jak sklonować listę generyczną w C#?
Mam ogólną listę obiektów w C# i chcę ją sklonować. Elementy z listy można klonować, ale nie ma opcji do wykonania list.Clone()
.
23 answers
Możesz użyć metody rozszerzenia.
static class Extensions
{
public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-23 19:11:35
Jeśli twoje elementy są typami wartości, możesz po prostu zrobić:
List<YourType> newList = new List<YourType>(oldList);
Jeśli jednak są to typy referencyjne i chcesz mieć głęboką kopię (zakładając, że twoje elementy poprawnie zaimplementują ICloneable
), możesz zrobić coś takiego:
List<ICloneable> oldList = new List<ICloneable>();
List<ICloneable> newList = new List<ICloneable>(oldList.Count);
oldList.ForEach((item) =>
{
newList.Add((ICloneable)item.Clone());
});
Oczywiście, zastąp ICloneable
w powyższych generikach i wrzuć dowolny typ elementu, który implementuje ICloneable
.
Jeśli twój Typ elementu nie obsługuje ICloneable
, ale ma Konstruktor kopiujący, możesz to zrobić zamiast:
List<YourType> oldList = new List<YourType>();
List<YourType> newList = new List<YourType>(oldList.Count);
oldList.ForEach((item)=>
{
newList.Add(new YourType(item));
});
Osobiście unikałbym ICloneable
ze względu na potrzebę zagwarantowania głębokiej kopii wszystkich członków. Zamiast tego sugerowałbym Konstruktor kopiujący lub metodę fabryczną, taką jak YourType.CopyFrom(YourType itemToCopy)
, która zwraca nową instancję YourType
.
Każda z tych opcji może być opakowana metodą (rozszerzenie lub inaczej).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-21 17:06:22
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;
}
Jest to jeden ze sposobów, aby to zrobić z C# i. NET 2.0. Twój obiekt wymaga [Serializable()]
. Celem jest utrata wszelkich odniesień i budowanie nowych.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-09 13:21:47
Dla płytkiej kopii, możesz zamiast tego użyć metody GetRange klasy generic List.
List<int> oldList = new List<int>( );
// Populate oldList...
List<int> newList = oldList.GetRange(0, oldList.Count);
Cytowane z: Generyki receptur
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-07 14:15:37
Po drobnej modyfikacji można również sklonować:
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-03 06:25:43
Jeśli nie potrzebujesz rzeczywistego klonu każdego pojedynczego obiektu wewnątrz List<T>
, najlepszym sposobem klonowania listy jest utworzenie nowej listy ze starą listą jako parametrem kolekcji.
List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);
Zmiany na myList
takie jak insert lub remove nie będą miały wpływu na cloneOfMyList
i odwrotnie.
Rzeczywiste obiekty, które zawierają te dwie listy, są jednak nadal takie same.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-10 14:09:54
Użycie Automapp (lub dowolnej lib mapowania, którą wolisz) do klonowania jest proste i łatwe do utrzymania.
Zdefiniuj swoje odwzorowanie:
Mapper.CreateMap<YourType, YourType>();
Czyń magię:
YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-13 23:20:22
Aby sklonować listę wystarczy zadzwonić .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 }
>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-25 00:35:03
Jeśli zależy Ci tylko na typach wartości...
I znasz typ:
List<int> newList = new List<int>(oldList);
Jeśli nie znasz wcześniej typu, będziesz potrzebował funkcji helpera:
List<T> Clone<T>(IEnumerable<T> oldList)
{
return newList = new List<T>(oldList);
}
Sprawiedliwy:
List<string> myNewList = Clone(myOldList);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-15 13:08:48
Jeśli odwołałeś się już do Newtonsoft.Json w Twoim projekcie i twoje obiekty są serializowalne zawsze możesz użyć:
List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))
Prawdopodobnie nie jest to najskuteczniejszy sposób, ale jeśli nie robisz tego 100 z 1000 razy, możesz nawet nie zauważyć różnicy prędkości.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-01 14:43:56
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-25 16:22:23
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;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-07 07:04:00
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;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-10 07:40:51
Można również po prostu przekonwertować listę na tablicę za pomocą ToArray
, a następnie sklonować tablicę za pomocą Array.Clone(...)
.
W zależności od twoich potrzeb, metody zawarte w klasie Array mogą zaspokoić Twoje potrzeby.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-15 15:34:44
Możesz użyć metody rozszerzenia:
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;
}
};
}
Możesz klonować wszystkie obiekty używając ich wartości typu members na przykład, rozważ tę 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
}
};
Uwaga: Jeśli dokonasz jakiejkolwiek zmiany podczas kopiowania (lub klonowania), nie wpłynie to na oryginalny obiekt.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-26 09:08:21
Jeśli potrzebujesz sklonowanej listy o tej samej pojemności, możesz spróbować tego:
public static List<T> Clone<T>(this List<T> oldList)
{
var newList = new List<T>(oldList.Capacity);
newList.AddRange(oldList);
return newList;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-16 12:02:38
Mój przyjaciel Gregor Martinovic i ja wymyśliliśmy to łatwe rozwiązanie przy użyciu JavaScript Serializer. Nie ma potrzeby, aby oznaczyć klasy jako Serializowalne i w naszych testach przy użyciu Newtonsoft JsonSerializer nawet szybciej niż przy użyciu BinaryFormatter. Z metodami rozszerzeń używanymi na każdym obiekcie.
Standardowa opcja. Net JavascriptSerializer:
public static T DeepCopy<T>(this T value)
{
JavaScriptSerializer js = new JavaScriptSerializer();
string json = js.Serialize(value);
return js.Deserialize<T>(json);
}
Faster option using Newtonsoft JSON :
public static T DeepCopy<T>(this T value)
{
string json = JsonConvert.SerializeObject(value);
return JsonConvert.DeserializeObject<T>(json);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-16 12:22:51
Zrobiłem dla mojego własnego rozszerzenia, które konwertuje kolekcję elementów, które nie implementują 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();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-03 12:41:06
Używam automapper do kopiowania obiektu. Właśnie ustawiłem mapowanie, które mapuje jeden obiekt do siebie. Możesz owinąć tę operację, jak chcesz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-13 14:28:08
Poniższy kod powinien zostać przeniesiony na listę z minimalnymi zmianami.
Zasadniczo działa poprzez wstawianie nowej liczby losowej z większego zakresu z każdą kolejną pętlą. Jeśli istnieją już liczby, które są takie same lub wyższe od niego, przesuń te liczby losowe o jeden, aby przenieść je do nowego większego zakresu indeksów losowych.
// 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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-16 11:56:03
Inna sprawa: przydałoby się odbicie. Jeśli pamięć podręczna będzie prawidłowo, to sklonuje 1 000 000 obiektów w ciągu 5,6 sekundy (niestety, 16,4 sekundy z obiektami wewnętrznymi).
[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;
}
Zmierzyłem to w prosty sposób, używając klasy Watcher.
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;
Wynik: z wewnętrznym obiektem PersonInstance - 16.4, PersonInstance = null-5.6
CopyFactory to tylko moja klasa testowa, gdzie mam kilkanaście testów, w tym użycie wyrażenia. Możesz to zaimplementować w innej formie w przedłużeniu czy jakoś tak. Nie zapomnij o buforowaniu.
Nie testowałem jeszcze serializacji, ale wątpię w poprawę z milionem klas. Spróbuję czegoś szybkiego protobuf / newton.P. S.: ze względu na prostotę czytania, użyłem tutaj tylko auto-własności. Mogę zaktualizować FieldInfo, lub powinieneś łatwo zaimplementować to samodzielnie.
Ostatnio przetestowałem bufory protokołu serializer z funkcją DeepClone po wyjęciu z pudełka. Wygrywa z 4,2 sekund na milionie prostych obiektów, ale jeśli chodzi o obiekty wewnętrzne, wygrywa z wynikiem 7,4 sekundy.
Serializer.DeepClone(personList);
Podsumowanie: Jeśli nie masz dostępu do klas, to pomoże. W przeciwnym razie zależy to od liczby obiektów. Myślę, że przydałoby się odbicie do 10 000 obiektów (może trochę mniej), ale za więcej niż to bufory protokołu serializer sprawdzą się lepiej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-16 12:02:11
Istnieje prosty sposób na klonowanie obiektów w C# przy użyciu serializera JSON i deserializera.
Możesz utworzyć klasę rozszerzenia:
using Newtonsoft.Json;
static class typeExtensions
{
[Extension()]
public static T jsonCloneObject<T>(T source)
{
string json = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(json);
}
}
Do klonowania i obiektowania:
obj clonedObj = originalObj.jsonCloneObject;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-16 12:23:34
//try this
List<string> ListCopy= new List<string>(OldList);
//or try
List<T> ListCopy=OldList.ToList();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-18 04:58:56