Deep copy of a list with class elements - c#

EDIT:
To whoever marked the question as duplicate. That question is for how to create a deep copy. My question was how to make sure a the copy constructor is called when copying a list of class elements.
I'm trying to make a deep copy of a List that contain custom class elements. If I have a List of strings I can just use
List<string> secondList = new List<string>(firstList);
and then freely modify the elements in the second list without effeting the ones in the firwst list. But when I try to do the same with a custom class type both lists get changed. To try and solve it I made a small test program that just has this class.
class TestClass
{
public string name;
public TestClass(string n)
{
name = n;
}
public TestClass(TestClass original)
{
name = original.name;
}
}
And all my program does is this
TestClass t = new TestClass("Name1");
List<TestClass> list1 = new List<TestClass>();
list1.Add(t);
List<TestClass> list2 = new List<TestClass>(list1);
list2[0].name = "Name2";
That last line of code changes the name of the first element in both lists, which I do no want.

The issue here is that your objects are reference types, and the lists hold references to those objects.
This means that even though your second list has a COPY of the references from the first list, the references are still pointing to the original objects.
In order to solve this, you must clone not the references in the lists but instead the actual objects that you have stored in the lists.
You have already defined a copy constructor for your class, so you can use that to make a deep copy of the list as follows:
var list2 = list1.Select(item => new TestClass(item)).ToList();

You create a reference with this line of Code:
List<TestClass> list2 = new List<TestClass>(list1);
But you won't like to use Call-by-Reference. You Need Call-by-Value
in this Approach.
so the working code in lambda-expression is the following one:
TestClass t = new TestClass("Name1");
List<TestClass> list1 = new List<TestClass>();
list1.Add(t);
List<TestClass> list2 = new List<TestClass>();
list2 = list1.Select(item => new TestClass(item)).ToList();
list2[0].name = "Name2";
Have fun with it...

Related

How add a List of Elements into a list without reference?

I cannot find an answer because it seems too much specific. So here's my issue with C#.
We can add another list to another one as a clone like this
list2 = new List<int>(list1);
What I want to know is how can I add a List into another one without any reference of the child?
List<List<List<int>>> wireCoords = new List<List<List<int>>>();
List<int> coord = new List<int>();
for(int i = 0; i < inputSplits.Length; i++)
{
coord.Add(0);
coord.Add(0);
wireCoords[i].Add(coord);
}
AS soon the wireCoords[0][0] list change, it also change inside wireCoords[1][0]
How can I fix this?
Two things. You cannot access a List via [i] accessor unless it has content at that index. Second, you can copy the values of a list by using List1.AddRange(List2). After this, changing List2 will not change List1.
In your for loop, the number of items grow to inputSplits.Length * 2 for every index of wiredCoords. To explain why this happens, lets take an example.
List<int> object1 = new List<int>();
object1.Add(1);
object1.Add(2);
List<int> object2 = object1;
object1.Add(3);
// at this time, object2 also has an element 3.
Console.WriteLine(string.Join(",", object2));
output:
1,2,3 (instead of 1,2 that you'd normally expect)
object1 never gets assigned the "value" of object2. object1 will get "reference" of the object2 and anywhere in code when you change values of object1, object2 will automatically get updated.
Fix for that could be
List<int> object2 = object1;
object1 = new List<int>(); // re-initialized
object1.Add(3);
// object1 has only 1 element
// object2 has 2 elements.
To resolve this, you create a new object or re-initialize the object to get a new reference and then use that for later assignments.
Your code:
List<List<List<int>>> wireCoords = new List<List<List<int>>>();
List<int> coord ;
for(int i = 0; i < inputSplits.Length; i++)
{
coord = new List<int>();
coord.Add(0);
coord.Add(0);
wireCoords.Add(coord);
}
Hope it helps.
Adding to extensive explanation of the issue by #Jawad, here is how you can do that with LINQ in more concise and functional way:
List<List<List<int>>> wireCoords = inputSplits
.Select(_=> new List<int>(){0,0})
.Select(coords=> new List<List<int>>(){coords})
.ToList();

How to make a deep copy of a List of Object

Hi I have a list of objects(list1) and I'd like to make a copy of this list but I want that the objects in the second list(list2) to not be linked to the one in first list.
this is what I do
list<Obj> list1 = new list<Obj>{};
// I fill the list1 with objects Obj
// now I want to make a deep copy this is what I do
list<Obj> list2 = new list<Obj>(list1);
// but when I edit an object in list 1 I also edit the object in list2
I'd like to be able to edit the objects in list1 without edititng the object in list2,how can I get that???
thanks for your answers
You should implement the ICloneable interface in your Obj class. After that, you can call this code to create your second list:
list<Obj> list2 = new list<Obj>(list1.Select(x => x?.Clone()));
It will clone every item in the list. (With null-check)
You could add a copy constructor to your Obj.
Then loop through list1 create a new instance of your objects using the copy constructor and add it to list2.
Example:
Copy constructor:
public Obj(Obj copyFrom)
{
this.field1 = copyFrom.field1;
.
.
}
With the following LINQ query you have a one liner to use the above contructor:
list2.AddRange(list1.Select(s => new Obj(s)));

How to unchain items in list<t> class

can some one explain to me how to break the chain with a NEW statement?
Let me clarify the chain I’m talking about. When I call to a class I use the NEW statement like so
Myclass x =new Myclass();
My understanding is this creates a new empty instance of Myclass. Now correct me if I’m wrong but having a new empty instance one should be able to add what ever data the class supports?
I use this lot and would think the above to be true until adding data in such a manner
Myclass x =new Myclass();
//oldMyclass being old data that needs to be changed then
//added back to the class as a new or duplicate entry
x = oldMyclass[1];
//we change the data
x.red= 0x54;
//we add it back
oldMyclass.add(x);
All is good until we edit the data after adding it say we need to change another value.
We access the oldMyclass and select the proper item say its index is 2 but we only want to change the values of index 2
Myclass x =new Myclass();
x = oldMyclass[2];
x.red=soemvalue;
oldMyclass[2] = x;
This will change the red value of both index 1 and index 2. How can I break the chain between index 1 and index 2?
I think I might have over simplified this question let me know.
Thanks for any information.
Edit: Here is the copy method that I tried
public static Items.SavedItem Copy(Items.SavedItem old)
{
Items.SavedItem x = new Items.SavedItem();
x.generator = old.generator;
x.hireling_class = old.hireling_class;
x.id = old.id;
x.item_slot = old.item_slot;
x.owner_entity_id = old.owner_entity_id;
x.socket_id = old.socket_id;
x.square_index = old.square_index;
x.used_socket_count = old.used_socket_count;
return x;
}
So let's say, for arguments sake, you have a class like this:
public MyClass
{
public string Foo { get; set; }
}
And you have a collection
List<MyClass> myList = new List<MyClass>();
Now you create an instance of MyClass
MyClass obj1 = new MyClass() { Foo = "bar" };
Now if you do this:
myList.Add(obj1);
myList.Add(obj1);
You now have a list with TWO members, but they happen to be the same object. Whats stored in the list is a reference to the object you added, not the object itself. So myList[0] == myList[1]
Now if you did this:
MyClass item = myList[1];
And then:
item.Foo = "something else";
Both the item at index 1 and the item at index 0 will have 'Foo == "something else"' because they are the same item.
Another point that seems to be confusing you is this: myList has two items. If I do this:
MyClass item = myList[0];
myList still has two items. Indexing a collection doesn't remove it and because of that, there is no need to add the item back to the list. It's already there. All I've done is copy the reference from myList to a variable named item.
There are collections (Stack and Queue for example) that do work on the principle that you will remove items and (potentially) add them back, but List doesn't work that way.
So if you wanted to add multiple objects to myList you need to create multiple objects with the new keyword. For example:
List<MyClass> myList = new List<MyClass>();
MyClass obj1 = new MyClass() { Foo = "bar" };
myList.Add(obj1);
obj1 = new MyClass() { Foo = "something else" }; // Note: I've reused the variable, but this is a *new* object
myList.Add(obj1);
Or, if you don't need the new object assigned to a variable, you can simply if to:
List<MyClass> myList = new List<MyClass>();
myList.Add(new MyClass() { Foo = "a" });
myList.Add(new MyClass() { Foo = "b" });
Or even more compactly, you can exploit the collection initialization syntax and simply:
List<MyClass> myList = new List<MyClass>()
{
new MyClass() { Foo = "a" },
new MyClass() { Foo = "b" }
}
If you want to copy an object from your list, then you need to copy each property (and if it contains other objects, you may need to copy them too). There are various ways to do this, IClonable or a copy constructor are examples, but it basically comes down to, at some point, doing something like this:
myCopy.Foo = myOriginal.Foo;
myCopy.Bar = myOriginal.Bar;
// repeat for all properties that you want to copy.
Now assuming that Foo and Bar aren't also reference types, you have a copy. If they are reference types, you have a copy, but myCopy.Foo and myOriginal.Foo are still pointing at the same object.

How to assign List<T> without it being a reference to the original List<T>?

For example
List<string> name_list1 = new List<string>();
List<string> name_list2 = new List<string>();
later in the code:
name_list1.Add("McDonald");
name_list1.Add("Harveys");
name_list1.Add("Wendys");
name_list2 = name_list1; // I make a copy of namelist1 to namelist2
So, from this point I would like to keep adding element or making changes in name_list2 without affecting name_list1. How do I do that?
name_list2 = new List<string>(name_list1);
This will clone the list.
Edit: This solution only works for primitive types. For objects, see other responses below.
Another Options is : Deep Cloning
public static T DeepCopy<T>(T item)
{
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
formatter.Serialize(stream, item);
stream.Seek(0, SeekOrigin.Begin);
T result = (T)formatter.Deserialize(stream);
stream.Close();
return result;
}
so,
you can use :
name_list2 = DeepCopy<List<string>>(name_list1);
OR:
name_list2 = DeepCopy(name_list1);
will also work.
For Primitive Types you can do this:
List<string> CopyList = new List<string>(OriginalList);
For non-primitve/user-difined types you can do this:
List<Person> CopyList = new List<Person>();
foreach(var item in OriginalList)
{
CopyList.Add(new Person {
Name = item.Name,
Address = item.Address
});
}
name_list2 = new List<string>(name_list1); // Clone list into a different object
At this point, the two lists are different objects. You can add items to list2 without affecting list1
The problem is the assignment. Until the assignment name_list2 = name_list1;, you have two different List objects on the heap pointed to by the variables name_list1 and name_list2. You fill up name_list1, which is fine. But the assignment says, "make name_list2 point to the same object on the heap as name_list1." The List that name_list2 used to point to is no longer accessible and will be garbage collected. What you really want is to copy the contents of name_list1 into name_list2. You can do this with List.AddRange. Note that this will result in a "shallow" copy, which is fine for the example you cite, where the list contents are strings, but may not be what you want when the list members are more complex objects. It all depends on your needs.
Based on #Mrunal answer I created an extension method:
public static T Clone<T>(this T source)
{
// Don't serialize a null object, simply return the default for that object
if (source == null)
{
return default;
}
// initialize inner objects individually
// for example in default constructor some list property initialized with some values,
// but in 'source' these items are cleaned -
// without ObjectCreationHandling.Replace default constructor values will be added to result
var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
And you can call it like this:
L2 = L1.Select(x => x.Clone()).ToList();
I like linq for this...
If list elements are primitives or structures then...
L2 = L1.ToList()
If list elements are classes then...
L2 = L1.Select(x => x.Copy()).ToList();
Where Copy could simply be a shallow copy exposure of MemberWiseClone, or it could be some implementation of a deep copy.
I prefer Json converter method to serialize and deserialize, this way you don't have to mark the classes for serialization, especially you have numerous child classes.
https://www.newtonsoft.com/json/help/html/SerializingJSON.htm
Here is an alternative solution:
List<string> name_list1 = new List<string>();
List<string> name_list2 = new List<string>();
name_list1.Add("McDonald");
name_list1.Add("Harveys");
name_list1.Add("Wendys");
name_list2.AddRange(name_list1.ToArray());
The ToArray() method copies 'name_list1' to a new array, which we then add to name_list2 via the AddRange() method.
For primitive types:
List ClonedList = new list(OriginalList);
For non-primitive/User Defined types:
We need to perform a deep copy:
Deep Copy is used to make a complete deep copy of the internal reference types, for this we need to configure the object returned by MemberwiseClone().
Step1- In your class inherit from ICloneable:
public class MyClass:ICloneable
Step2- Implement method
public MyClass Clone()
{
MyClass MyClassObj =new MyClass();
MyClassObj.Property1 = this.Property1;
.
.
MyClassObj.Property_N = this.Property_N;
return MyClass;
}
Step3- now clone your List
List<MyClass> MyClassClone = new List<MyClass>();
for(i=0; i<Count; i++)
{
MyClassClone.Add(OriginalClaaObj[i].Clone());
}
This will make deep copy of each item of the object.
None of the above solutions worked for me when using lists of class objects.
This can be used for copying any object to another object with shared property names.
public static void ObjectToObject(object source, object destination)
{
// Purpose : Use reflection to set property values of objects that share the same property names.
Type s = source.GetType();
Type d = destination.GetType();
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
var objSourceProperties = s.GetProperties(flags);
var objDestinationProperties = d.GetProperties(flags);
var propertyNames = objSourceProperties
.Select(c => c.Name)
.ToList();
foreach (var properties in objDestinationProperties.Where(properties => propertyNames.Contains(properties.Name)))
{
try
{
PropertyInfo piSource = source.GetType().GetProperty(properties.Name);
properties.SetValue(destination, piSource.GetValue(source, null), null);
}
catch (Exception ex)
{
throw;
}
}
}
public static List<T> CopyList<T>(this List<T> lst)
{
List<T> lstCopy = new List<T>();
foreach (var item in lst)
{
var instanceOfT = Activator.CreateInstance<T>();
ObjectToObject(item, instanceOfT);
lstCopy.Add(instanceOfT);
}
return lstCopy;
}
For lists use this:
list2 = list1.CopyList();
If both the lists are of the same complex type then you can do something like below:-
SomeClass List2 = new List();
List1.ForEach(u => List2.Add(u));
What I am doing is to loop through each element of List1 and keep adding it to List2.
I believe this is the shortest way to do it.
While it could be potential performance-threat solution, but it would copy the values property-by-property eloquently.
using Newstonsoft.Json;
ClassA classA = new ClassA();
ClassA classACopyWithoutReference = JsonConvert.DeserializeObject<ClassA>(JsonConvert.SerializeObject(classA));
this solution works For complex objects (Replace T with name of your Type):
list2 = list1.Concat(new List<T> { object }).ToList();
or:
list2 = list1.ToArray().Append(object).ToList()
You can clone the complex object by serialize and deserialize it, it will remove you object reference and create new object without reference
using Newstonsoft.Json;
List<string> name_list1 = new List<string>();
name_list1.Add("McDonald");
name_list1.Add("Harveys");
name_list1.Add("Wendys");
name_list2 = name_list1;
List<string> name_list2 = JsonConvert.DeserializeObject<List<string>>
(JsonConvert.SerializeObject(name_list1)); // Ii make a copy of namelist1 to namelist2
this is working for me using LINQ...
lst1= lst2.ToList();

ArrayList Manipulation?

it is possible to store data in of two arraylist into <list>?
here's my code with two arrays that will merge:
ArrayList arrPrices = new ArrayList();
List<StockInfoPrice> lstStockInfoPrice = new List<StockInfoPrice>();
Util oUtils = new Util();
arrPrices = oUtils.GetPrices(SymbolIndex);
ArrayList arrDetails = new ArrayList();
List<StockInfoDetails> lstStockInfoDetails = new List<StockInfoDetails>();
Util oUtils = new Util();
arrPrices = oUtils.GetDetails(SymbolIndex);
You can do it with linq simply:
lstStockInfoPrice.AddRange(arr1.Cast<StockInfoPrice>());
lstStockInfoPrice.AddRange(arr2.Cast<StockInfoPrice>());
See Cast in IEnumerable.
It is possible.
You could try the following if oUtils.GetPrices(SymbolIndex) returns StockInfoPrice;
lstStockInfoPrice.AddRange(oUtils.GetPrices(SymbolIndex));
I this Util class isn't your own, then you're stuck with Marius' answer. However, if you control that Util class then you could make the GetPrices and GetDetails methods return someting with type IEnumerable and IEnumerable respectively.
Then, you can add the whole lot to another list with List.AddRange() method.
As an aside, your allocation in the declaration of arrPrices is a waste of time - the allocated object is never used and will then be subject to garbage collection.
Your GetPrices() method returns an ArrayList - ie, a new arrayList, and
arrPrices = oUtils.GetPrices(SymbolIndex);
simply makes arrPrices refer to the new list. There are then no references to the one you allocated when you declared arrPrices, so it's thrown away.
Do it like this:-
ArrayList arrPrices;
List<StockInfoPrice> lstStockInfoPrice = new List<StockInfoPrice>();
Util oUtils = new Util();
arrPrices = oUtils.GetPrices(SymbolIndex);
If you want to move the value from arrPrices to lstStockInfoPrice and lstStockInfoDetails, you could iterate over the array list and put the elements in the list. Something like this:
foreach(var o in arrPrices)
{
lstStockInfoPrice.Add(o); // or Add((StockInfoPrice)o)
}

Categories

Resources