Generating a new instance of a List in C# - c#

I have a problem with using C#, if I initialize a certain list, lets say List<T> exampleList using another pre-existing list, lets say toModify like this: List<T> exampleList = new List<T>(toModify). When I later modify toModify list the newly created list also modifies itself. If it passes the value by reference shouldn't the value of exampleList stay the same since it was generated from the other one?
TLDR: Value of a list I initialize using another list(second list) changes when I change the second list. I come from a Java background and can't understand why this happens. Will I always have to use clone?

Let us use this example :
List<A> firstList = new List<A>()
{
new A() { Id = 3 },
new A() { Id = 5 }
};
List<A> secondList = new List<A>(firstList);
secondList[1].Id = 999;
Console.WriteLine(firstList[1].Id);
Output : 999
The main reason for this is that even though we created a new List<T> that points to a new memory allocated on heap it still works with references the point to same objects.
To create a list that points to new (!) objects with the same values we'd need to clone these elements somehow, one way to do it is to use LINQ .Select() method in order to create new objects and then a ToList() method to copy the list itself:
List<A> firstList = new List<A>()
{
new A() { Id = 3 },
new A() { Id = 5 }
};
List<A> secondList = firstList.Select(el => new A() { Id = el.Id }).ToList();
secondList[1].Id = 999;
Console.WriteLine(firstList[1].Id);
Output : 5

Yes.
You're creating a new list containing the same items as the old list. If you clear the first list, the items in the second stay.
But if you change a property for one of the items in the first list, then it is the same object in the second list.
So, both list are referencing the same items in memory. When you write list1[0].SomeProperty = 1 you're changing that using object reference that is the same in list2, so changes are reflected in the second list.
For how to clone a List and generate new references for items, check this SO Answer.

In the following line:
List<T> exampleList = new List<T>(toModify)
you create a list of T calling List<T>'s constructor that takes one argument of type IEnumerable<T>. For further info on the latter, please have a look here.
Method's arguments in C# are passed by default by value and not by reference. They can be passed by reference, but you have to explicitly state this in the signature of the corresponding method using the ref keyword and at the point you call this method, using again the same keyword. So the toModify is passed by value to the constructor of List<T>.
What's the importance of this?
In C# types can be divided into two categories (despite the fact that all types inherit from the System.Object):
Value types
Reference types
When we pass a value type as an argument, we pass a copy of it's value. Each modification we make in either the original value or in the copy of the original value is not reflected to one another. On the other hand, when we pass a reference type as an argument, we pass a copy of that reference. So now we have two references (pointers) that point to the same location in memory. That being said, it's clear that if we change any property of the object in which both references points to, this would be visible by both of them.
In your case, this is what is happening. toModify is a list of reference types (under the hood you have an array, whose items are references to other objects). So any change to the items of the initial list, toModify, is reflected to the list you construct based on this list.
A simple example that you could use to verify the above is the following:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString() => $"X: {X}, Y: {Y}";
}
class Program
{
static void Main(string[] args)
{
var listA = new List<int> {1, 2, 3};
var listB = new List<int>(listA);
// Before the modification
Console.WriteLine(listA[0]); // prints 1
Console.WriteLine(listB[0]); // prints 1
listA[0] = 2;
// After the mofication
Console.WriteLine(listA[0]); // prints 2
Console.WriteLine(listB[0]); // prints 1
Console.ReadKey();
var pointsA = new List<Point>
{
new Point {X = 3, Y = 4},
new Point {X = 4, Y = 5},
new Point {X = 6, Y = 8},
};
var pointsB = new List<Point>(pointsA);
// Before the modification
Console.WriteLine(pointsA[0]); // prints X: 3, Y: 4
Console.WriteLine(pointsB[0]); // prints X: 3, Y: 4
pointsA[0].X = 4;
pointsA[0].Y = 3;
// After the modification
Console.WriteLine(pointsA[0]); // prints X: 4, Y: 3
Console.WriteLine(pointsB[0]); // prints X: 4, Y: 3
Console.ReadKey();
}
}

Related

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.

Using static variable in C#

I have problem when using static variable in my project (force using static variable)
public static List<int> a = new List<int>();
public static List<List<int>> list = new List<List<int>>();
public Form1()
{
for (int i = 0; i < 5;i++ )
a.Add(i);
list.Add(a);
Console.WriteLine(list[0].Count); // **count = 5**
a.RemoveAt(0);
list.Add(a);
Console.WriteLine(list[0].Count); // **count = 4**
Console.WriteLine(list[1].Count); // count = 4
}
When I use a.RemoveAt(0) , it makes list[0] change. Why does it do this and how can I fix it?
Well yeah, you're referring to the same object because List<T> is a reference type. See: http://msdn.microsoft.com/en-us/library/s6938f28.aspx
For example:
List<int> a = new List<int>();
List<int> b = a;
Console.WriteLine(Object.ReferenceEquals(a, b)); //true
a.Add(1);
Console.WriteLine(a[0]); //1
Console.WriteLine(b[0]); //1
a[0] = 9000;
Console.WriteLine(a[0]); //9000
Console.WriteLine(b[0]); //9000
Storing a list in a list will yield the same result: you are pointing to the same original list.
If you want to store a copy of a in list[0], then make a copy:
list.Add(new List<int>(a));
Or use linq to make it more succinct:
list.Add(a.ToList());
(make sure to add a using System.Linq; directive to the top of your code file)
You must understand it from basics. Lists objects work through reference. When you added object a to list, that means you added a reference of a to list. Now what ever you change in a that will be reflected in list[0] also as it is referring to same reference.
To achieve this, you can do some like this.
var masterList = new List<List<int>>();
var l1 = new List<int>{1, 2, 3, 4, 5}; // Reference created for l1
var l2 = new List<int>(); // Reference created for l2
masterList.Add(l1); // l1 reference added to masterList
masterList.Add(l2); // l2 reference added to masterList
l2.AddRange(l1); // This will copy values from l1 reference to l2 reference and will not touch the references
l2.RemoveAt(0); // First value removed from reference l2 (And therefore it should not affect reference l1)
MessageBox.Show(masterList[0].Count.ToString() + " and " + masterList[1].Count.ToString());
It must help you to understand whats happening here. You must also remember that it has NOTHING to do with static variables as your question heading indicates.
Hope it helps.
list is keeping a reference to a so if you change the object a then the count in list will also change because it refers to the same thing.
What you need to do is make a copy of a and pass that into list, allowing list to keep a separate reference to a and allowing you to keep the count the same.
Example:
list.Add(a.ToList()); // See Chris Sinclair's example, full credit to him
you need to make copy of list(because list is keep+ing a reference to a):
public static List<int> a = new List<int>();
public static List<List<int>> list = new List<List<int>>();
for (int i = 0; i < 5; i++)
a.Add(i);
list.Add(a.Select(i => i).ToList());//passed in a copy of a.
Console.WriteLine(list[0].Count); // **count = 5**
a.RemoveAt(0);
list.Add(a);
Console.WriteLine(list[0].Count); // **count = 5**
Console.WriteLine(list[1].Count); // count = 4

Adding items to an IEnumerable through an extension method does not work?

In most of the methods I use that return some kind of collection I return IEnumerable rather than the specific type (e.g. List). In many cases I have another collection that I want to combine with the result IEnumerable, this would be exactly like taking a List and adding another List to it using the AddRange method. I have the following example, in it I have created an extension method that should take a collection of items to add and adds them to a base collection, while debugging this appears to works but in the original collection the items are never added. I don't understand this, why aren't they added, is there something about the implementation of the IEnumerable that I am missing? I understand that IEnumerable is a read only interface, but Iam not adding to this list in the example below, I am replacing it, but the original IEnumerable does not change.
class Program
{
static void Main(string[] args)
{
var collectionOne = new CollectionContainerOne();
var collectionTwo = new CollectionContainerTwo();
// Starts at 1- 50 //
collectionOne.Orders.AddRange(collectionTwo.Orders);
// Should now be 100 items but remains original 50 //
}
}
public class CollectionContainerOne
{
public IEnumerable<Order> Orders { get; set; }
public CollectionContainerOne()
{
var testIds = Enumerable.Range(1, 50);
var orders = new List<Order>();
foreach (int i in testIds)
{
orders.Add(new Order() { Id = i, Name = "Order #" + i.ToString() });
}
this.Orders = orders;
}
}
public class CollectionContainerTwo
{
public IEnumerable<Order> Orders { get; set; }
public CollectionContainerTwo()
{
var testIds = Enumerable.Range(51, 50);
var orders = new List<Order>();
foreach (int i in testIds)
{
orders.Add(new Order() { Id = i, Name = "Order #" + i.ToString() });
}
this.Orders = orders;
}
}
public class Order
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return this.Name;
}
}
public static class IEnumerable
{
public static void AddRange<T>(this IEnumerable<T> enumerationToAddTo, IEnumerable<T> itemsToAdd)
{
var addingToList = enumerationToAddTo.ToList();
addingToList.AddRange(itemsToAdd);
// Neither of the following works //
enumerationToAddTo.Concat(addingToList);
// OR
enumerationToAddTo = addingToList;
// OR
enumerationToAddTo = new List<T>(addingToList);
}
}
You are modifying the parameter enumerationToAddTo, which is a reference. However, the reference is not itself passed by reference, so when you modify the reference, the change is not observable in the caller. Furthermore, it is not possible to use ref parameters in extension methods.
You are better off using Enumerable.Concat<T>. Alternatively, you can use ICollection, which has an Add(T) method. Unfortunately, List<T>.AddRange isn't defined in any interface.
Here is an example to illustrate the passing of reference types by reference. As Nikola points out, this is not really useful code. Don't try this at home!
void Caller()
{
// think of ss as a piece of paper that tells you where to find the list.
List<string> ss = new List<string> { "a", "b" };
//passing by value: we take another piece of paper and copy the information on ss to that piece of paper; we pass that to the method
DoNotReassign(ss);
//as this point, ss refers to the same list, that now contains { "a", "b", "c" }
//passing by reference: we pass the actual original piece of paper to the method.
Reassign(ref ss);
// now, ss refers to a different list, whose contents are { "x", "y", "z" }
}
void DoNotReassign(List<string> strings)
{
strings.Add("c");
strings = new List<string> { "x", "y", "z" }; // the caller will not see the change of reference
//in the piece of paper analogy, we have erased the piece of paper and written the location
//of the new list on it. Because this piece of paper is a copy of SS, the caller doesn't see the change.
}
void Reassign(ref List<string> strings)
{
strings.Add("d");
//at this point, strings contains { "a", "b", "c", "d" }, but we're about to throw that away:
strings = new List<string> { "x", "y", "z" };
//because strings is a reference to the caller's variable ss, the caller sees the reassignment to a new collection
//in the piece of paper analogy, when we erase the paper and put the new object's
//location on it, the caller sees that, because we are operating on the same
//piece of paper ("ss") as the caller
}
EDIT
Consider this program fragment:
string originalValue = "Hello, World!";
string workingCopy = originalValue;
workingCopy = workingCopy.Substring(0, workingCopy.Length - 1);
workingCopy = workingCopy + "?";
Console.WriteLine(originalValue.Equals("Hello, World!"); // writes "True"
Console.WriteLine(originalValue.Equals(workingCopy); // writes "False"
If your assumption about reference types were true, the output would be "False" then "True"
Calling your extensions method like this:
collectionOne.Orders.AddRange(collectionTwo.Orders);
Is essentially the same as:
IEnumerable.AddRange(collectionOne.Orders, collectionTwo.Orders);
Now what happens there, is you pass copy of reference to the collectionOne.Orders to the AddRange method. In your AddRange implementation you try to assign new value to the copy. It gets "lost" inside. You are not assigning new value to collectionOne.Orders, you assign it to its local copy - which scope is only within the method body itself. As a result of all modifications happenining inside AddRange, outside world notices no changes.
You either need to return new enumerable, or work on lists directly. Having mutating methods on IEnumerable<T> is rather counterintuitive, I'd stay away from doing that.
What you want exists and is called Concat. Essentially, when you do this in your Main:
var combined = collectionOne.Orders.Concat(collectionTwo.Orders);
Here, combined will refer to an IEnumerable that will traverse both source collections when enumerated.
IEnumerable does not support adding. What you in essence did in your code is create new collection from your enumerable, and add items to that new collection. Your old collection still has same items.
E.g., you create a collection of numbers like this
Collection1 = [ 1, 2, 3, 4, 5 ]
when you do Collection1.ToList().Add(...) you will get new collection with same members, and add new members like so:
Collection1 = [ 1, 2, 3, 4, 5, 6, 7, ... ]
your old collection will however still hold old members, as ToList creates new collection.
Solution #1:
Instead of using IEnumerable use IList which supports modification.
Solution #2 (bad):
Cast your IEnumerable back to it's derived type and add members to it. This is quite bad though, in fact it's better to just return List in the first place
IEnumerable<Order> collectionOne = ...;
List<Order> collectionOneList = (List<Order>)collectionOne;
collectionOneList.Add(new Order());
General guideline (best):
If you are returning collections which are standard in .NET there is no reason to return their interfaces. In this case it's best to use original type. If you are however returning collection which you implemented yourself, then you should return an interface
It's a completely different case when you are thinking about input parameters. If your method is asking to enumerate over items, then you should ask for IEnumerable. This way you can do what you need over it, and you are placing least constraint on person who is calling it. They can send any enumerable. If you need to add to that collection, you may require IList so that you can also modify it in your method.
Basically the problem is that you can't assign a value to enumerationToAddTo partially because it isn't a reference parameter. Also as phoog mentions ToList() creates a new list and does not cast the existing IEnumerable to a list.
This isn't really a good use of a extension. I would recommend that you add a method to your container collection that allows you add add new items to the IEnumerable instance. This would better encapsulate the logic that's particular to that class.

List<List<int>> Remove() method

I'd like to use Remove() method on list of lists, but it's not working for me.
Simple example should say everything:
List<List<int>> list = new List<List<int>>();
list.Add(new List<int> { 0, 1, 2 });
list.Add(new List<int> { 1, 2 });
list.Add(new List<int> { 4 });
list.Add(new List<int> { 0, 1, });
list.Remove(new List<int> { 1, 2 });
If I use RemoveAt(1) it works fine but Remove() not.
It is obviously the same reason that this code returns false:
List<int> l1 = new List<int>();
List<int> l2 = new List<int>();
l1.Add(1);
l2.Add(1);
bool b1 = l1 == l2; // returns False
bool b2 = l1.Equals(l2); // returns False too
So it seems to me that I cannot simply compare two lists or even arrays. I can use loops instead of Remove(), but there must be easier way.
Thanks in advance.
The problem is that List<T> doesn't override Equals and GetHashCode, which is what List<T> will use when trying to find an item. (In fact, it will use the default equality comparer, which means it'll use the IEquatable<T> implementation if the object implements it, and fall back to object.Equals/GetHashCode if necessary). Equals will return false as you're trying to remove a different object, and the default implementation is to just compare references.
Basically you'd have write a method to compare two lists for equality, and use that to find the index of the entry you want to remove. Then you'd remove by index (using RemoveAt). EDIT: As noted, Enumerable.SequenceEqual can be used to compare lists. This isn't as efficient as it might be, due to not initially checking whether the counts are equal when they can be easily computed. Also, if you only need to compare List<int> values, you can avoid the virtual method call to an equality comparer.
Another alternative is to avoid using a List<List<int>> in the first place - use a List<SomeCustomType> where SomeCustomType includes a List<int>. You can then implement IEquatable<T> in that type. Note that this may well also allow you to encapsulate appropriate logic in the custom type too. I often find that by the type you've got "nested" collection types, a custom type encapsulates the meaning of the inner collection more effectively.
First approach:
List<int> listToRemove = new List<int> { 1, 2 };
list.RemoveAll(innerList => innerList.Except(listToRemove).Count() == 0);
This also removes the List { 2, 1 }
Second approach (preferred):
List<int> listToRemove = new List<int> { 1, 2 };
list.RemoveAll(innerList => innerList.SequenceEqual(listToRemove));
This removes all lists that contain the same sequence as the provided list.
List equality is reference equality. It won't remove the list unless it has the same reference as a list in the outer list. You could create a new type that implements equality as set equality rather than reference equality (or you do care about order as well?). Then you could make lists of this type instead.
This simply won't work because you're tying to remove a brand new list (the new keyword kind of dictates such), not one of the ones you just put in there. For example, the following code create two different lists, inasmuch as they are not the same list, however much they look the same:
var list0 = new List<int> { 1, 2 };
var list1 = new List<int> { 1, 2 };
However, the following creates one single list, but two references to the same list:
var list0 = new List<int> { 1, 2 };
var list1 = list0;
Therefore, you ought to keep a reference to the lists you put in there should you want to act upon them with Remove in the future, such that:
var list0 = new List<int> { 1, 2 };
listOfLists.Remove(list0);
They are different objects. Try this:
List<int> MyList = new List<int> { 1, 2 };
List<List<int>> list = new List<List<int>>();
list.Add(new List<int> { 0, 1, 2 });
list.Add(MyList);
list.Add(new List<int> { 4 });
list.Add(new List<int> { 0, 1, });
list.Remove(MyList);
You need to specify the reference to the list you want to remove:
list.Remove(list[1]);
which, really, is the same as
list.RemoveAt(1);

Why does setting a property on an enumerated object not work?

I know a lot about C# but this one is stumping me and Google isn't helping.
I have an IEnumerable range of objects. I want to set a property on the first one. I do so, but when I enumerate over the range of objects after the modification, I don't see my change.
Here's a good example of the problem:
public static void GenericCollectionModifier()
{
// 1, 2, 3, 4... 10
var range = Enumerable.Range(1, 10);
// Convert range into SubItem classes
var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i});
Write(items); // Expect to output 1,2,3,4,5,6,7,8,9,10
// Make a change
items.First().MagicNumber = 42;
Write(items); // Expect to output 42,2,3,4,5,6,7,8,9,10
// Actual output: 1,2,3,4,5,6,7,8,9,10
}
public static void Write(IEnumerable<SubItem> items)
{
Console.WriteLine(string.Join(", ", items.Select(item => item.MagicNumber.ToString()).ToArray()));
}
public class SubItem
{
public string Name;
public int MagicNumber;
}
What aspect of C# stops my "MagicNumber = 42" change from being output? Is there a way I can get my change to "stick" without doing some funky converting to List<> or array?
Thanks!
-Mike
When you call First() it enumerates over the result of this bit of code:
Select(i => new SubItem() {Name = "foo", MagicNumber = i});
Note that the Select is a lazy enumerator, meaning that it only does the select when you ask for an item from it (and does it every time you ask it). The results are not stored anywhere, so when you call items.First() you get a new SubItem instance. When you then pass items to Write, it gets a whole bunch of new SubItem instances - not the one you got before.
If you want to store the result of your select and modify it, you need to do something like:
var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i}).ToList();
I suspect something going in the background. Most likely due to the fact the IEnumerables can only be iterated once.
Does it work if you add a 'ToList()' after the call to Select() when assigning to 'items'?
The only thing I can think of is that items.First() passes a copy of SubItem to your instead of the reference, so when you set it the change isn't carried through.
I have to assume it has something to do with IQueryable only being able to be iterated once. You may want to try changing this:
// Convert range into SubItem classes
var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i});
to
// Convert range into SubItem classes
var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i}).ToList();
And see if there are any different results.
You can't/shouldn't modify a collection through an enumerator. I'm surprised this doesn't throw an exception.
.First() is a method, not a property. It returns a new instance of the object in the first position of your Enumerable.

Categories

Resources