12

I have this code (C#):

using System.Collections.Generic;

namespace ConsoleApplication1
{
    public struct Thing
    {
        public string Name;
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<Thing> things = new List<Thing>();
            foreach (Thing t in things) //  for each file
            {
                t.Name = "xxx";
            }
        }
    }
}

It won't compile.
The error is:

Cannot modify members of 't' because it is a 'foreach iteration variable'

If I change Thing to a class rather than a struct, however, it does compile.

Please can someone explain what's going on?

1

4 Answers 4

12

More or less what it says, the compiler won't let you change (parts of) the looping var in a foreach.

Simply use:

for(int i = 0; i < things.Count; i+= 1) //  for each file
{
    things[i].Name = "xxx";
}

And it works when Thing is a class because then your looping var is a reference, and you only make changes to the referenced object, not to the reference itself.

Sign up to request clarification or add additional context in comments.

Comments

8

A struct is no reference type but a value type.

If you would have a class instead of a struct for Thing, the foreach loop would create a reference variable for you, that would point to the correct element in you list. But since it is a value type, it only operates on a copy of your Thing, which is in this case the iteration variable.

2 Comments

Yes, it is relevant. Because it is a value type, the entire struct is the looping variable and therefore no part of it can be modified.
Thanks for explaining the differene, Johannes. Much appreciated. +1
4

An alternate syntax that I prefer to @Henk's solution is this.

DateTime[] dates = new DateTime[10];

foreach(int index in Enumerable.Range(0, dates.Length))
{
   ref DateTime date = ref dates[index];

   // Do stuff with date.
   // ...
}

     

If you are doing a reasonable amount of work in the loop then not having to repeat the indexing everywhere is easier on the eye imo.

P.S. DateTime is actually a really poor example as it doesn't have any properties you can set, but you get the picture.

UPDATE

Works with arrays for me.

struct A       
{
   public string Name;
}

var list = new A[]
{
   new A { Name = "Bob" },
   new A { Name = "Bob" }
};

foreach (int index in Enumerable.Range(0, list.Length))
{
   ref A itemRef = ref list[index];
   itemRef.Name = "Bill";
}

1 Comment

This doesn't seem to work with a list; Indexer access returns temporary value. 'ref' argument must be an assignable variable, field or an array element
1

A struct is a value type but a class is a reference type. That's why it compiles when This is a class but not when it is a struct

See more: http://www.albahari.com/valuevsreftypes.aspx

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.