35

In one place i am using the list of string in that case the i am able to change the value of the string as code given below,

foreach(string item in itemlist.ToList())
{
    item = someValue; //I am able to do this 
}

But for object of class i am not able to alter the members value of the object the code is as below,

public class StudentDTO
{
    string name;
    int rollNo;
}

studentDTOList=GetDataFromDatabase();

foreach(StudentDTO student in studentDTOList.ToList())
{
      student = ChangeName(student); //Not working 
}

private StudentDTO ChangeName(StudentDTO studentDTO)
{
     studentDTO.name = SomeName;
     return studentDTO;
}

Error is : Can not assign because it's iteration variable

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • 19
    No, you're really not able to do the code in the first snippet. The iteration variable is readonly. – Jon Skeet Jul 16 '13 at 12:51
  • i am able to do because of .ToList() making the copy of list and iterating through that copy so it's working –  Jul 16 '13 at 12:53
  • 3
    No, you're not able to do that. Just try it. The code *will not compile*. If you really believe it will, please produce a short but complete program demonstrating that - you'll see it fails. – Jon Skeet Jul 16 '13 at 12:58
  • 1
    you could *in loop* modify the `list` in the `ToList()` clone then after the loop simply `assign/set` the `list` to equal the new `ToList()` modified product – Mr Heelis Feb 09 '18 at 09:57
  • 1
    just a side note, acronyms greater than 2 letters should use PascalCase. ie your class should be called StudentDto – David Klempfner Dec 11 '18 at 03:12

3 Answers3

39

You cannot change the iteration variable of a foreach-loop, but you can change members of the iteration variable. Therefore change the ChangeName method to

private void ChangeName(StudentDTO studentDTO)
{
    studentDTO.name = SomeName;
}

Note that studentDTO is a reference type. Therefore there is no need to return the changed student. What the ChangeName method gets, is not a copy of the student but a reference to the unique student object. The iteration variable and the studentDTOList both reference the same student object as does the studentDTO parameter of the method.

And change the loop to

foreach(StudentDTO student in studentDTOList)
{
    ChangeName(student);
}

However, methods like ChangeName are unusual. The way to go is to encapsulate the field in a property

private string name;
public string Name
{
    get { return name; }
    set { name = value; }
}

You can then change the loop to

foreach(StudentDTO student in studentDTOList)
{
    student.Name = SomeName;
}

EDIT

In a comment you say that you have to change many fields. In that case it would be okay to have a method UpdateStudent that would do all the changes; however I still would keep the properties.

If there is no additional logic in the properties besides passing through a value, you can replace them by the handy auto-implemented properties.

public string Name { get; set; }

In that case you would have to drop the field name.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • 2
    so i dont have to return anything from the function as it already getting reference type of argument –  Jul 16 '13 at 13:41
  • 1
    Yes. Even if you return the student, you can safely ignore the return value and not assign it to the iteration variable. Returning the student allows you to chain the method calls: `student.UpdateValues().StoreChanges().PrintReport();` – Olivier Jacot-Descombes Jul 16 '13 at 13:59
21

You're not actually changing the object that you're referring to anyway, so you can just use:

foreach (StudentDTO student in studentDTOList)
{
    student.name = SomeName;
}

Or still call a method:

foreach (StudentDTO student in studentDTOList)
{
    ChangeStudent(student);
}

In both cases, the code doesn't change the value of the iteration variable (student) so it's okay.

But your original example doesn't compile anyway - an iteration variable introduced by a foreach loop is read-only.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • name will be private currently but +1 either way – Sayse Jul 16 '13 at 12:54
  • Actualy i am giving an example for code my original code is changing lots of values from that variable but with plane foreach () it's not working it shows error . –  Jul 16 '13 at 12:56
  • 2
    @user2553512: "it shows error" is *never* enough information... and it's fine to change lots of data within the object, you just can't assign a new value to the iteration variable itself. So you could call `ChangeStudent(student)` which would change the data - you just can't do `student = ChangeStudent(student)`. – Jon Skeet Jul 16 '13 at 12:57
  • That's what i am doing if you look at the second loop with object that's how i am doing it by assigning return value to the iteration variable so is it possible by using .ToList() –  Jul 16 '13 at 12:59
  • So @JonSkeet, student = ChangeStudent(student) is compiler restriction. Is there any specific reason behind this behavior...i cannot think of??? – Dinesh Jul 16 '13 at 13:01
  • 1
    @DnshPly9: Mostly because it almost never does what you want it to. It wouldn't change the thing you're iterating over, for example... – Jon Skeet Jul 16 '13 at 13:02
  • @user2553512 its definitly not possible. your first code snippet does not compile. `Cannot assign to 'item' because it is a 'foreach iteration variable'` – Jehof Jul 16 '13 at 13:02
  • @user2553512: No, you *can't* assign a new value to the iteration variable, but you don't *need* to. You're just changing the data within the object - you'd be assigning the same value back. – Jon Skeet Jul 16 '13 at 13:02
  • so how should i do it ? –  Jul 16 '13 at 13:11
  • 1
    @user2553512: I've already shown you that, in the answer, right from the start. – Jon Skeet Jul 16 '13 at 14:01
0

Instead of Foreach, I used For loop, this way I also get the Index that is needed to be changed, Then I call a function, In which I pass the Data to be Changed as well as the index to be changed from:

  for(int i = 0; i< myList.Count; i++)
  {
    if(mylist[i] == otherData)
    {
      //call the function
      ChangeData(otherData, i);
    }
  }
  
  
  
  
  
  public void ChangeData(DataType DataToChangeInto, int i)
  {
    mylist[i] = DataToChangeInto;
  }
Saad Aamir
  • 21
  • 4