ChatGPT解决这个技术问题 Extra ChatGPT

How do I clone a generic list in C#?

I have a generic list of objects in C#, and wish to clone the list. The items within the list are cloneable, but there doesn't seem to be an option to do list.Clone().

Is there an easy way around this?

You should say if you're looking for a deep copy or a shallow copy
What are deep and shallow copies?
@orip Isn't clone() by definition a deep copy? In C# you can pass pointers around easily with =, I thought.
@Chris a shallow copy copies one level deeper than pointer copy. Eg a shallow copy of a list will have the same elements, but will be a different list.

J
Jeff Yates

If your elements are value types, then you can just do:

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

However, if they are reference types and you want a deep copy (assuming your elements properly implement ICloneable), you could do something like this:

List<ICloneable> oldList = new List<ICloneable>();
List<ICloneable> newList = new List<ICloneable>(oldList.Count);

oldList.ForEach((item) =>
    {
        newList.Add((ICloneable)item.Clone());
    });

Obviously, replace ICloneable in the above generics and cast with whatever your element type is that implements ICloneable.

If your element type doesn't support ICloneable but does have a copy-constructor, you could do this instead:

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

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

Personally, I would avoid ICloneable because of the need to guarantee a deep copy of all members. Instead, I'd suggest the copy-constructor or a factory method like YourType.CopyFrom(YourType itemToCopy) that returns a new instance of YourType.

Any of these options could be wrapped by a method (extension or otherwise).


I think List.ConvertAll might look nicer than creating a new list and doing a foreach+add.
@Dimitri: No, that's not true. The problem is, when ICloneable was defined, the definition never stated whether the clone was deep or shallow, so you cannot determine which type of Clone operation will be done when an object implements it. This means that if you want to do a deep clone of List<T>, you will have to do it without ICloneable to be sure it is a deep copy.
Why not use the AddRange method? (newList.AddRange(oldList.Select(i => i.Clone()) or newList.AddRange(oldList.Select(i => new YourType(i))
@phoog: I think that it is a little less readable/understandable when scanning the code, that's all. Readability wins for me.
@JeffYates: One insufficiently-considered wrinkle is that things generally only need to be copied if there exists some execution path which would mutate them. It's very common to have immutable types hold a reference to an instance of mutable type, but never expose that instance to anything that will mutate it. Needless copying of things that aren't ever going to change can sometimes be a major performance drain, increasing memory usage by orders of magnitude.
n
nawfal

You can use an extension method.

static class Extensions
{
    public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
    {
        return listToClone.Select(item => (T)item.Clone()).ToList();
    }
}

I think List.ConvertAll might do this in faster time, since it can pre-allocate the entire array for the list, versus having to resize all the time.
@MichaelGG, what if you don't want to Convert but just Clone/Duplicate the items in the list? Would this work? || var clonedList = ListOfStrings.ConvertAll(p => p);
@IbrarMumtaz: That is the same as var clonedList = new List(ListOfStrings);
Nice solution! By the way I prefer public static List CLone... It is more useful in the cases like this, because no further cast needed: List cloned = listToClone.Clone();
Isn't this just half the answer since it relies on an implementation of ICloneable, which would be the important part of the question?
J
Jochem

For a shallow copy, you can instead use the GetRange method of the generic List class.

List<int> oldList = new List<int>( );
// Populate oldList...

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

Quoted from: Generics Recipes


You can also achieve this by using the List's contructor to specify a List from which to copy from. eg var shallowClonedList = new List(originalList);
I often use List<int> newList = oldList.ToList(). Same effect. However, Arkiliknam's solution is best for readability in my opinion.
@DanBechard Years later, but I prefer ToList as it avoids all the redundancies - I wonder which is actually more performant... looked it up. Looks list ToList calls new List<T> which ultimately will use Array.CopyTo, so about the same.
A
AustinWBryan
public static object DeepClone(object obj) 
{
    object objResult = null;

    using (var ms = new MemoryStream())
    {
        var bf = new BinaryFormatter();
        bf.Serialize(ms, obj);

        ms.Position = 0;
        objResult = bf.Deserialize(ms);
     }

     return objResult;
}

This is one way to do it with C# and .NET 2.0. Your object requires to be [Serializable()]. The goal is to lose all references and build new ones.


+1 - i like this answer - it is quick, dirty, nasty and very effective. I used in silverlight, and used the DataContractSerializer as the BinarySerializer was not available. Who needs to write pages of object cloning code when you can just do this? :)
I like this. While it's nice to do things "right", quick and dirty often comes in handy.
Quick! but: Why dirty?
This deep clones and is fast and easy. Carefull on other suggestions on this page. I tried several and they don't deep clone.
Only negative aspect, if you can call it that, is that your classes have to be marked Serializable for this to work.
X
Xavier John

To clone a list just call .ToList(). This creates a shallow copy.

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

A little warning this is a shallow copy ... This will create two list objects, but the objects inside will be the same. I.e. changing one property will change the same object / property in the original list.
P
Peter Mortensen

After a slight modification you can also clone:

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

Do not forget the T should be serializable, otherwise you get System.Runtime.Serialization.SerializationException.
Good answer. One hint: You could add if (!obj.GetType().IsSerializable) return default(T); as the first statement which prevents the exception. And if you change it to an extension method, you could even use the Elvis operator like var b = a?.DeepClone(); (given var a = new List<string>() { "a", "b" }; for example).
J
Jader Feijo

Unless you need an actual clone of every single object inside your List<T>, the best way to clone a list is to create a new list with the old list as the collection parameter.

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

Changes to myList such as insert or remove will not affect cloneOfMyList and vice versa.

The actual objects the two Lists contain are still the same however.


I agree with user49126, I'm seeing that it is a shallow copy and changes made to one list are reflected in the other list.
@Seidleroni, you are wrong. The changes made to the list itens are afected on the other list, changes in the list itself are not.
This is shallow copy.
How is this a shallow copy?
@WellingtonZanelli Just confirmed that removing an element from myList removes it from cloneOfMyList as well.
D
Derek Liang

Use AutoMapper (or whatever mapping lib you prefer) to clone is simple and a lot maintainable.

Define your mapping:

Mapper.CreateMap<YourType, YourType>();

Do the magic:

YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);

L
Lucas B

If you only care about value types...

And you know the type:

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

If you don't know the type before, you'll need a helper function:

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

The just:

List<string> myNewList = Clone(myOldList);

This doesn't clone the elements.
P
ProfNimrod

If you have already referenced Newtonsoft.Json in your project and your objects are serializeable you could always use:

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

Possibly not the most efficient way to do it, but unless you're doing it 100s of 1000s of times you may not even notice the speed difference.


It's not about the speed difference, it's about the readability. If I came to this line of code I would slap my head and wonder why they introducted a third-party library to serialize and then deserialize an object which I would have no idea why it's happening. Also, this wouldn't work for a model list with objects that have a circular structure.
This code worked excellently for me for deep cloning. The app is migrating document boilerplate from Dev to QA to Prod. Each object is a packet of several document template objects, and each document in turn is comprised of a list of paragraph objects. This code let me serialize the .NET "source" objects and immediately deserialize them to new "target" objects, which then get saved to a SQL database in a different environment. After tons of research, I found lots of stuff, much of which was too cumbersome, and decided to try this. This short and flexible approach was "just right"!
z
ztorstri

For a deep copy, ICloneable is the correct solution, but here's a similar approach to ICloneable using the constructor instead of the ICloneable interface.

public class Student
{
  public Student(Student student)
  {
    FirstName = student.FirstName;
    LastName = student.LastName;
  }

  public string FirstName { get; set; }
  public string LastName { get; set; }
}

// wherever you have the list
List<Student> students;

// and then where you want to make a copy
List<Student> copy = students.Select(s => new Student(s)).ToList();

you'll need the following library where you make the copy

using System.Linq

you could also use a for loop instead of System.Linq, but Linq makes it concise and clean. Likewise you could do as other answers have suggested and make extension methods, etc., but none of that is necessary.


That's called a "copy constructor". It's a v error prone approach, whenever you add a new field to Student, you have to remember to add it to the copy constructor. The main idea behind "clone" is to avoid that problem.
Even with ICloneable, you have to have a "Clone" method on your class. Unless you use reflection (which you could also use in the above approach), that Clone method is going to look real similar to the copy constructor approach above, and will suffer from the same issue of having to update for new/changed fields. But that's saying "The class has to be updated when fields of the class change". Of course it does ;)
There is a SERIOUS LIMITATION with this approach. Consider class StudentB : Student. In a list of Students, some might be StudentBs. new Student(s) will do the wrong thing - it will create a Student, not a StudentB. ICloneable does not have this limitation. (assuming StudentB also implements ICloneable.)
F
F.H.

There is no need to flag classes as Serializable and in our tests using the Newtonsoft JsonSerializer even faster than using BinaryFormatter. With extension methods usable on every object.

attention: private members are not cloned

Standard .NET JavascriptSerializer option:

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

Private members are not cloned using the JSON method. stackoverflow.com/a/78612/885627
Not sure if this helps anyone. In my case the JSON option was not working (exception: "System.InvalidCastException: Unable to cast object of type 'Newtonsoft.Json.Linq.JObject' to type ...") but it worked when I changed the last line slightly, like this: public static T DeepCloneJSON(this T value) { string json = JsonConvert.SerializeObject(value); return (T)JsonConvert.DeserializeObject(json, value.GetType()); }
J
John Kurtz

I'll be lucky if anybody ever reads this... but in order to not return a list of type object in my Clone methods, I created an interface:

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

Then I specified the extension:

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

And here is an implementation of the interface in my A/V marking software. I wanted to have my Clone() method return a list of VidMark (while the ICloneable interface wanted my method to return a list of object):

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

And finally, the usage of the extension inside a class:

private List<VidMark> _VidMarks;
private List<VidMark> _UndoVidMarks;

//Other methods instantiate and fill the lists

private void SetUndoVidMarks()
{
    _UndoVidMarks = _VidMarks.Clone();
}

Anybody like it? Any improvements?


C
Cody Gray
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;
}

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

s
shahrooz.bazrafshan
    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;
        }
    }

P
Peter Mortensen

If you need a cloned list with the same capacity, you can try this:

public static List<T> Clone<T>(this List<T> oldList)
{
    var newList = new List<T>(oldList.Capacity);
    newList.AddRange(oldList);
    return newList;
}

2
2 revs
 //try this
 List<string> ListCopy= new List<string>(OldList);
 //or try
 List<T> ListCopy=OldList.ToList();

M
Martin Zaloga

If I need deep copy of collection, I have favorite approach like this:

public static IEnumerable<T> DeepCopy<T>(this IEnumerable<T> collectionToDeepCopy)
{
    var serializedCollection = JsonConvert.SerializeObject(collectionToDeepCopy);
    return JsonConvert.DeserializeObject<IEnumerable<T>>(serializedCollection);
}

Serializing data to text is very resource expensive compared to the actual job that needs to be done. This can be ok if you are not working on production code tho, just a one-time thing.
A
Athafoud

You can use extension method:

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

}

You can clone all objects by using their value type members for example, consider this class:

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

Note: if you do any change on copy (or clone) it will not affect the original object.


This is a very basic example that has no use in real world programming. You have to clone a list of complex objects with children of lists of other objects and so on.
T
Thomas Cerny

Using a cast may be helpful, in this case, for a shallow copy:

IList CloneList(IList list)
{
    IList result;
    result = (IList)Activator.CreateInstance(list.GetType());
    foreach (object item in list) result.Add(item);
    return result;
}

applied to generic list:

List<T> Clone<T>(List<T> argument) => (List<T>)CloneList(argument);

D
Dan H

I use automapper to copy an object. I just setup a mapping that maps one object to itself. You can wrap this operation any way you like.

http://automapper.codeplex.com/


A
Avi

You could also simply convert the list to an array using ToArray, and then clone the array using Array.Clone(...). Depending on your needs, the methods included in the Array class could meet your needs.


This does not work; changes to the values in the cloned array STILL change the values in the original list.
you can use var clonedList = ListOfStrings.ConvertAll(p => p); as given by @IbrarMumtaz .... Works effectively... Changes to one list are kept to itself and doesn't to reflect in another
K
Kamil Budziewski

I've made for my own some extension which converts ICollection of items that not implement 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();
    }
}

seems some collections (e.g. DataGrid's SelectedItems at Silverlight) skip the implementation of CopyTo which is a problem with this approach
P
Peter Mortensen

The following code should transfer onto a list with minimal changes.

Basically it works by inserting a new random number from a greater range with each successive loop. If there exist numbers already that are the same or higher than it, shift those random numbers up one so they transfer into the new larger range of random indexes.

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

P
Peter Mortensen

Another thing: you could use reflection. If you'll cache this properly, then it'll clone 1,000,000 objects in 5.6 seconds (sadly, 16.4 seconds with inner objects).

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

I measured it in a simple way, by using the Watcher class.

 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;

RESULT: With inner object PersonInstance - 16.4, PersonInstance = null - 5.6

CopyFactory is just my test class where I have dozen of tests including usage of expression. You could implement this in another form in an extension or whatever. Don't forget about caching.

I didn't test serializing yet, but I doubt in an improvement with a million classes. I'll try something fast protobuf/newton.

P.S.: for the sake of reading simplicity, I only used auto-property here. I could update with FieldInfo, or you should easily implement this by your own.

I recently tested the Protocol Buffers serializer with the DeepClone function out of the box. It wins with 4.2 seconds on a million simple objects, but when it comes to inner objects, it wins with the result 7.4 seconds.

Serializer.DeepClone(personList);

SUMMARY: If you don't have access to the classes, then this will help. Otherwise it depends on the count of the objects. I think you could use reflection up to 10,000 objects (maybe a bit less), but for more than this the Protocol Buffers serializer will perform better.


P
Peter Mortensen

There is a simple way to clone objects in C# using a JSON serializer and deserializer.

You can create an extension class:

using Newtonsoft.Json;

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

To clone and object:

obj clonedObj = originalObj.jsonCloneObject;

Z
Zeyad

For a deep clone I use reflection as follows:

public List<T> CloneList<T>(IEnumerable<T> listToClone) {
    Type listType = listToClone.GetType();
    Type elementType = listType.GetGenericArguments()[0];
    List<T> listCopy = new List<T>();
    foreach (T item in listToClone) {
        object itemCopy = Activator.CreateInstance(elementType);
        foreach (PropertyInfo property in elementType.GetProperties()) {
            elementType.GetProperty(property.Name).SetValue(itemCopy, property.GetValue(item));
        }
        listCopy.Add((T)itemCopy);
    }
    return listCopy;
}

You can use List or IEnumerable interchangeably.