Consider this:
List<MyClass> obj_list = get_the_list();
foreach( MyClass obj in obj_list )
{
obj.property = 42;
}
Is obj a reference to the corresponding object within the list so that when I change the property the change will persist in the object instance once constructed somewhere?
Yes, obj is a reference to the current object in the collection (assuming MyClass is in fact a class). If you change any properties via the reference, you're changing the object, just like you would expect.
Be aware however, that you cannot change the variable obj itself as it is the iteration variable. You'll get a compile error if you try. That means that you can't null it and if you're iterating value types, you can't modify any members as that would be changing the value.
The C# language specification states (8.8.4)
"The iteration variable corresponds to
a read-only local variable with a
scope that extends over the embedded
statement."
Yes, until you change the generic type from List to IEnumerable..
You've asked 2 different questions here, lets take them in order.
Does a foreach loop iterate by reference?
If you mean in the same sense as a C++ for loop by reference, then no. C# does not have local variable references in the same sense as C++ and hence doesn't support this type of iteration.
Will the change be persisted
Assuming that MyClass is a reference type, the answer is yes. A class is a reference type in .Net and hence the iteration variable is a reference to the one variable, not a copy. This would not be true for a value type.
Well, it happened to me that my changes were not updated in a foreach loop when I iterated through var collection:
var players = this.GetAllPlayers();
foreach (Player player in players)
{
player.Position = 1;
}
When I changed var to List it started working.
You can in this instance (using a List<T>) but if you were to be iterating over the generic IEnumerable<T> then it becomes dependant on its implementation.
If it was still a List<T> or T[] for instance, all would work as expected.
The big gotcha comes when you are working with an IEnumerable<T> that was constructed using yield. In this case, you can no longer modify properties of T within an iteration and expect them to be present if you iterate the same IEnumerable<T> again.
Maybe it's interesting for you to lean that by version C# 7.3 it's possible to change values by reference provided that the enumerator's Current property returns a reference Type. The following would be valid (verbatim copy from the MS docs):
Span<int> storage = stackalloc int[10];
int num = 0;
foreach (ref int item in storage)
{
item = num++;
}
Read more about this new feature at
C# foreach statement | Microsoft Docs.
this is true as long as it is not a struct.
Well, without understanding exactly what you mean by "Iterate by reference", I can't answer specifically yes or no, but I can say that what's going on under the surface is that the .net framework is constructing an "enumerator" class for each time client code calls a foreach, for the life of the foreach, that maintains a reference pointer into the collection being iterated over, and each time your foreach iterates, ir "delivers" one item and "increments" the pointer or reference in the enumerator to the next item...
This happens regardless of whether the items in the collection you are iterating over are values types or reference types.
obj is a reference to an item inside the List, hence if you change it's value it will persist. Now what you should be concerned about is whether or not get_the_list(); is making a deep copy of the List or returning the same instance.
Yes, that's also why you cannot alter the enumerable object in the context of the foreach statement.
Related
I've created a struct defined below:
public struct Piece
{
string pieceType;
string pCode;
bool dead;
bool highlighted;
bool specialUsed;
bool moved;
Material defaultMaterial;
}
and in a separate subroutine have created a dictionary which will hold entries of this type:
Dictionary<string, Piece> pieceDict;
I'm attempting to populate it using a foreach loop like so:
GameObject[] pieces = GameObject.FindGameObjectsWithTag("Piece");
foreach (GameObject piece in pieces)
{
string pCode = GetPieceCode(piece);
pieceDict.Add(pCode, new Piece());
pieceDict[pCode].pCode = pCode;
//More properties once I get this working will go here.
}
However, it would appear that something is going wrong as it will not allow me to access the individual properties of the new entry. Getting this error:
Cannot modify the return value of 'Dictionary<string, Piece>.this[string]' because it is not a variable
I have looked at the documentation and can't work out where I'm going wrong, so any help would be appreciated!
I'm using the UnityEngine.
Structs are value types, when you pass a struct, it creates a copy of the struct. This is especially a problem with mutable structs since when you pass the struct to a method (or get it out of a property) the struct is copied by value and then you are modifying the copy, not the original.
The fix is to avoid mutable structs and to use a class instead. Only use structs where the value (of all its fields) is fixed during construction.
Edit
To expand on this a little bit, we can examine the line:
pieceDict[pCode].pCode = pCode;
What is happening here is that the first part, pieceDict[pCode] returns a copy of the value (in the case of a value type), which is then operated on by the .pCode part, which you assign it to pCode, but because you are working on a copy, and not what is stored in the dictionary, it will not be saved. The compiler is smart enough to notice that you are trying to assign to it and that it will be thrown away anyway, so it gives you the error.
In my opinion the error could be better worded, something like "assignment to a copy of a value type does not result in an assignment of the underlying value", because as a new-ish programmer when it says that the assignment fails because its not a variable is a little confusing. The dictionary is a variable, the item that went in is a variable, so its hard to understand why the value is not a variable.
object[] objArray = new object[]{"blah", 4, "whatever"};
foreach(var value in objArray) vs. foreach(object value in objArray)
I'm curious as to what the difference is between those, other than var must remain its type after assigned. Is one better than the other? Thanks.
From a purely functional perspective, var is just a shortcut for object here, since objArray's elements are of declared type object.
Using object is a sign to whoever's reading the code that the items in the array are not known to be any type more specific than object. The use of var does not connote this. In the minimal case you posted, it really doesn't make any difference to the clarity of the program which one you use.
If, on the other hand, it is not immediately clear from the context what type of object you are working with, then it may be advantageous to explicitly declare the type. On the other hand, if the type of the elements of the collection you're iterating over is verbose and obtrusive, then it may be advantageous to use var, so that the reader's eyes will be drawn to the logic of the code rather than a mess of generic parameters.
The only difference between var and any other type is that you let the compiler determine the type.
there is no difference between those two, var would be object in this case.
In your example no. But,
When declaring objects you get:
Boxing and Unboxing
However. When using var, it is compiled exactly as if you specified the exact type name.
So var tempval = 5; is the same as int tempval = 5;
var names = new[] {
new { Name = "John", Age = 44 },
new { Name = "Diana", Age = 45 },
new { Name = "James", Age = 17 },
new { Name = "Francesca", Age = 15}
};
for (int i = 0; i < names.Length; i++)
{
names[i].Age = 23; //-------->Error
names[i] = new { Name = "XYX", Age = 26 }; //----->Works fine
}
foreach(var name in names)
{
name.Age = 1; //-------->Error
name = new { Name = "ABC", Age = 25 }; //-------->Error
}
I have two questions here.
1. Why I was not able to change the any attribute of an iteration variable.
2. I was only able to assign a new object to the iteration variable in for loop. Not in foreach loop. Why?
Question 1: Why I was not able to change the any attribute of an iteration variable?
From the documentation on Anonymous Types:
Anonymous types provide a convenient way to encapsulate a set of read-only properties
You cannot change the values of the properties in your anonymous type, so
name.Age = 1;
// and
names[i].Age = 1;
are equally invalid.
Question 2. I was only able to assign a new object to the iteration variable in for loop. Not in foreach loop. Why?
From the documentation on IEnumerable:
An enumerator remains valid as long as the collection remains unchanged.
You would invalidate the iterator if you change the backing list in any way. Consider what would happen if the iterator returned the items in a specific order based on the Age field, for example.
Why I was not able to change the any attribute of an iteration variable.
You're using anonymous types, which always have read-only properties in C#. (In VB they're read/write by default but can be made read-only with the Key modifier.)
From the C# 4 spec, section 7.6.10.6:
An anonymous object initializer declares an anonymous type and returns an instance of that type. An anonymous type is a nameless class type that inherits directly from object. The members of an anonymous type are a sequence of read-only properties infverred from the anonymous object initializer used to create an instance of the type.
For your second question...
I was only able to assign a new object to the iteration variable in for loop. Not in foreach loop. Why?
The language specification defines it that way. In particular, even if you could change the variable, that wouldn't change the array, unless the language specification made it work just for arrays. In general, foreach uses IEnumerable/IEnumerator (or members looking like that) which only provides a "reading" view of the sequence.
From section 8.8.4 of the C# 4 spec:
The iteration variable corresponds to a read-only local variable with a scope that extends over the embedded statement.
(Importantly, although it's a single read-only variable, its value changes between iterations. In C# 5 this will be changed so that it's effectively a "new" variable on each iteration. The difference is only important when the variable is captured by something like a lambda expression.)
As for question 2): In the "works fine" case you are not changing the iteration variable. In that case, the iteration variable is i. What you do is replace one element of an array with a new element. This always works.
Because foreach uses an enumerator, and enumerators can't change the underlying collection, but can, however, change any objects referenced by an object in the collection. This is where Value and Reference-type semantics come into play.
On a reference type, that is, a class, all the collection is storing is a reference to an object. As such, it never actually touches any of the object's members, and couldn't care less about them. A change to the object won't touch the collection.
On the other hand, value types store their entire structure in the collection. You can't touch its members without changing the collection and invalidating the enumerator.
Moreover, the enumerator returns a copy of the value in the collection. In a ref-type, this means nothing. A copy of a reference will be the same reference, and you can change the referenced object in any way you want with the changes spreading out of scope. On a value-type, on the other hand, means all you get is a copy of the object, and thus any changes on said copy will never propagate.
The ArrayList class can only contain references to objects but what happens when you store a value type such as integers?
string str = "Hello";
int i = 50;
ArrayList arraylist = new ArrayList();
arraylist.Add(str); // Makes perfectly sense:
// Reference to string-object (instance) "Hello" is added to
// index number 0
arraylist.Add(i); // What happens here? How can a reference point to a value
// type? Is the value type automatically converted to an
// object and thereafter added to the ArrayList?
It's called "boxing": automagically the int is converted to a reference type. This does cost some performance.
See also Boxing and Unboxing.
If you pull up the ArrayList class in ILSpy, you'll see that the backing store is:
private object[] _items;
and that the Add method accepts an instance of type object:
public virtual int Add(object value) { ... }
So when you call Add with an integer, .NET boxes the integer and then it gets added to the _items array in the ArrayList as an object.
Incidentally, if you need an ArrayList of just integers and you are using the .NET 2.0 Framework or later, you should use the List<T> (a.k.a. generic List) class instead, which will perform better since it avoids having to box an int when storing or retrieving it from the list (see the Performance Considerations section in that last link).
Its called boxing. A "Box" holds a copy of the struct along with details of what type it is.
MSDN : http://msdn.microsoft.com/en-us/library/yz2be5wk%28v=vs.80%29.aspx
In framework 2.0 + microsoft gave us generics which are faster and more effictive:
MSDN : http://msdn.microsoft.com/en-us/library/ms172192.aspx
The Arraylist.Add() will adds take any value and adds as an object, so the integer value will be automatically converted(boxing) and is added in to arraylist.
Take a look at the following program:
class Test
{
List<int> myList = new List<int>();
public void TestMethod()
{
myList.Add(100);
myList.Add(50);
myList.Add(10);
ChangeList(myList);
foreach (int i in myList)
{
Console.WriteLine(i);
}
}
private void ChangeList(List<int> myList)
{
myList.Sort();
List<int> myList2 = new List<int>();
myList2.Add(3);
myList2.Add(4);
myList = myList2;
}
}
I assumed myList would have passed by ref, and the output would
3
4
The list is indeed "passed by ref", but only the sort function takes effect. The following statement myList = myList2; has no effect.
So the output is in fact:
10
50
100
Can you help me explain this behavior? If indeed myList is not passed-by-ref (as it appears from myList = myList2 not taking effect), how does myList.Sort() take effect?
I was assuming even that statement to not take effect and the output to be:
100
50
10
Initially, it can be represented graphically as follow:
Then, the sort is applied myList.Sort();
Finally, when you did: myList' = myList2, you lost the one of the reference but not the original and the collection stayed sorted.
If you use by reference (ref) then myList' and myList will become the same (only one reference).
Note: I use myList' to represent the parameter that you use in ChangeList (because you gave the same name as the original)
You are passing a reference to the list, but your aren't passing the list variable by reference - so when you call ChangeList the value of the variable (i.e. the reference - think "pointer") is copied - and changes to the value of the parameter inside ChangeList aren't seen by TestMethod.
try:
private void ChangeList(ref List<int> myList) {...}
...
ChangeList(ref myList);
This then passes a reference to the local-variable myRef (as declared in TestMethod); now, if you reassign the parameter inside ChangeList you are also reassigning the variable inside TestMethod.
Here is an easy way to understand it
Your List is an object created on heap. The variable myList is a
reference to that object.
In C# you never pass objects, you pass their references by value.
When you access the list object via the passed reference in
ChangeList (while sorting, for example) the original list is changed.
The assignment on the ChangeList method is made to the value of the reference, hence no changes are done to the original list (still on the heap but not referenced on the method variable anymore).
This link will help you in understanding pass by reference in C#.
Basically,when an object of reference type is passed by value to an method, only methods which are available on that object can modify the contents of object.
For example List.sort() method changes List contents but if you assign some other object to same variable, that assignment is local to that method. That is why myList remains unchanged.
If we pass object of reference type by using ref keyword then we can assign some other object to same variable and that changes entire object itself.
(Edit: this is the updated version of the documentation linked above.)
C# just does a shallow copy when it passes by value unless the object in question executes ICloneable (which apparently the List class does not).
What this means is that it copies the List itself, but the references to the objects inside the list remain the same; that is, the pointers continue to reference the same objects as the original List.
If you change the values of the things your new List references, you change the original List also (since it is referencing the same objects). However, you then change what myList references entirely, to a new List, and now only the original List is referencing those integers.
Read the Passing Reference-Type Parameters section from this MSDN article on "Passing Parameters" for more information.
"How do I Clone a Generic List in C#" from StackOverflow talks about how to make a deep copy of a List.
While I agree with what everyone has said above. I have a different take on this code.
Basically you're assigning the new list to the local variable myList not the global.
if you change the signature of ChangeList(List myList) to private void ChangeList() you'll see the output of 3, 4.
Here's my reasoning...
Even though list is passed by reference, think of it as passing a pointer variable by value
When you call ChangeList(myList) you're passing the pointer to (Global)myList. Now this is stored in the (local)myList variable. So now your (local)myList and (global)myList are pointing to the same list.
Now you do a sort => it works because (local)myList is referencing the original (global)myList
Next you create a new list and assign the pointer to that your (local)myList. But as soon as the function exits the (local)myList variable is destroyed.
HTH
class Test
{
List<int> myList = new List<int>();
public void TestMethod()
{
myList.Add(100);
myList.Add(50);
myList.Add(10);
ChangeList();
foreach (int i in myList)
{
Console.WriteLine(i);
}
}
private void ChangeList()
{
myList.Sort();
List<int> myList2 = new List<int>();
myList2.Add(3);
myList2.Add(4);
myList = myList2;
}
}
Use the ref keyword.
Look at the definitive reference here to understand passing parameters.
To be specific, look at this, to understand the behavior of the code.
EDIT: Sort works on the same reference (that is passed by value) and hence the values are ordered. However, assigning a new instance to the parameter won't work because parameter is passed by value, unless you put ref.
Putting ref lets you change the pointer to the reference to a new instance of List in your case. Without ref, you can work on the existing parameter, but can't make it point to something else.
There are two parts of memory allocated for an object of reference type. One in stack and one in heap. The part in stack (aka a pointer) contains reference to the part in heap - where the actual values are stored.
When ref keyword is not use, just a copy of part in stack is created and passed to the method - reference to same part in heap. Therefore if you change something in heap part, those change will stayed. If you change the copied pointer - by assign it to refer to other place in heap - it will not affect to origin pointer outside of the method.