It is well known in textbooks that the annotations in foreach are elements that cannot be altered during traversal
For example, declaring an array
int [] ii={0,1,2,3}; foreach (int in II) { 3; the error "M" is a "foreach iteration variable" and cannot be assigned a value of Console.WriteLine (m); }
As you can tell, we can't change the values inside the array, but the foreach statement is created for the collection, and the array is just one of the collections, and what about the other collections?
In C # We've created several collection classes,list<t>, arrays, and so on, and now we're using list<t> as a collection to validate our ideas, now we're going to create a type declaration as the product class.
Public classProduct { Public intId {Set;Get; } Public stringName {Set;Get; } Public stringCode {Set;Get; } PublicString Category {Get;Set; } Public decimalPrice {Get;Set; } PublicDateTime Productdate {Get;Set; } Public Override stringToString () {returnString.Format ("{0}{1}{2}{3}{4}{5}", This. Id.tostring (). PadLeft (3), This. Name.padleft ( One), This. Code.padleft ( One), This. Category.padleft (7), This. Price.tostring (). PadLeft (8), This. Productdate.tostring ("yyyy-m-d"). PadLeft ( -)); } }
View Code
In this class we rewrite the ToString method so that we can see the output better.
Now we declare an instance.
Product PR = new product (); pr. Id = 1; pr. Name = "Soap"; pr. Price = 1M; pr. Productdate = DateTime.Parse ("2015-02-14"); pr. Code = "0001"; pr. Category = "Daily Necessities";
Adding a PR to the collection list
list<product> list = new list<product> (); List. ADD (PR);
And then we traverse it.
foreach (Product PRD in list) { Console.WriteLine (PRD. ToString ()); }
The output is
Id trade name Product Code type price production date 0 soap 0001 daily necessities 1 2015-2-14
We're trying to iterate over the iteration variable
foreach (Product PRD in list) { Console.WriteLine (PRD. ToString ()); } foreach (Product PRD in list) { PRD. Id = 2; Console.WriteLine (PRD. ToString ()); }
The output is
Id trade name Product Code type price production date 0 soap 0001 daily necessities 1 2015-2-142 soap 0001 Daily Necessities 1 2015-2-14
The modification succeeds, the element of the iteration variable is changed successfully, we will PR at this time. ID output found that the PR ID has also been modified to 2, which is why?
We modified the PRD element not only to error, but also to change the original value. Is it because we modified the PRD. ID instead of PRD so did we succeed?
Well, let's do it again. Modify the product class to be a struct (public class product becomes public struct product). Run the above code again and find that even the compilation is not going through. The display "PRD" is a "foreach iteration variable cannot be assigned to it" ".
Now to explain the problem by analyzing the structure of the collection class, the collection class must include a public definition of "GetEnumerator" if it is to use the Foreach method, and the return value of the method is a enmerator<t> In layman's terms it's a combination class. To invoke this foreach method C # requires it to get an enumerator that is a type that he must have a bool MoveNext () method, T-current returns a property of type T, and the Void Reset () method. Every time we use the Foreach method, we call this enumerator type's method, we use the current property to return the variable of the currently iteration, because it only gets the value of the instance of product when we want to assign a value to the iteration variable, because it has only the Get method. Because we don't have a set method. But why can we modify the fields in the product class and not modify the fields in the product structure? This involves differences in memory between value types and reference types, and simply, if we have two variables on the heap, one is a value type and one is a reference type, when we make a PRD on a value type variable (that is, a struct). ID, we are now in the position of the value type instance on the heap, if we only get the value of the Get method can not be modified, but if we are a reference type, when we are the reference type instance (that is, the class) to make PRD. ID, because the reference type variable stores only one address, we PRD. The location of the ID is transferred directly to the field of the instance, not to this variable, so we use PRD. The ID is not the iteration variable, but the instance of the iteration variable is obtained.
Think about it. This is because a member of a reference type A is an instance of type a if it contains a value type member B and a reference type member C, if you want to prevent the modification of type A, instance B of type B cannot be modified because B is stored in a. And a in the reference type C instance C can be modified, because a inside stores the address of instance C, modify the content of instance C does not modify the address of instance C inside a.
The following is the code for product Collection Class Productcollection, which is from the blog of the predecessor Zhang Ziyang. We are interested to know.
#regionProduct collection type Public classProductcollection:ienumerable<product> { //storing product using a hash table PrivateHashtable table; /// <summary> ///constructor to add an instance of the product class/// </summary> /// <param name= "Array" >Product Instance</param> PublicProductcollection (paramsproduct[] Array) {Table=NewHashtable (); foreach(Product ppinchArray) { This. ADD (PP); } } /// <summary> ///Indexer/// </summary> /// <param name= "index" ></param> /// <returns></returns> PublicProduct This[intIndex] { Get { stringSelected =GetKey (index); returnTable[selected] asProduct; } Set { stringSelected =GetKey (index); Table[selected]=value; } } PublicProduct This[stringKey] { Get{String selected=GetKey (key); returnTable[selected] asProduct; } Set { stringSelected =GetKey (key); Table[selected]=value; } } Private stringGetKey (intindex) { if(Index <0|| Index >=table. Count) {Throw NewException ("Index over range! "); } inti =0; stringSelected =""; foreach(stringIteminchtable. Keys) {if(i = =index) {Selected=item; Break; } I++; } returnselected; } Private stringGetKey (stringkey) { foreach(stringKinchtable. Keys) {if(key = = k)returnK; } Throw NewException ("The key value does not exist"); } Public voidAdd (Product item) {foreach(stringKeyinchtable. Keys) {if(Key = =item. Code) {Throw NewException (item. Code +"Product code cannot be duplicated"); }} table. ADD (item. Code, item); } Public intCount {Get { returntable. Count; } } Public voidInsert (intindex, PRODUCT Item) { } Public voidRemove (Product item) {}/// <summary> ///returns an enumerator/// </summary> /// <returns>Enumerator</returns> PublicIenumerator<product>GetEnumerator () {return NewProductenumerator ( This); } IEnumerator Ienumerable.getenumerator () {return NewProductenumerator ( This); } Public classProductenumerator:ienumerator<product> { Public ReadOnlyproductcollection Collection; Private intindex; PublicProductenumerator (Productcollection collection) { This. Collection =collection; Index= -1; } PublicProduct Current {Get { returnCollection[index]; } } ObjectIEnumerator.Current {Get { returnCollection[index]; } } Public BOOLMoveNext () {index++; if(Index >=collection. Count) {return false; } Else return true; } Public voidReset () {index= -1; } Public voidDispose () {}}} #endregion
View Code
Research on the encapsulation of foreach for iterative variables