30

I have the following code:

newsplit.ToList().ForEach(x => x = "WW");

I would expect that all elements in the list are now "WW" but they are still the original value. How come? What do I have to do different?

0

5 Answers 5

42

Assuming that newsplit is an IEnumerable<string>, you want:

newsplit = newsplit.Select(x => "WW");

The code that you currently have is equivalent to the following:

foreach(string x in newsplit.ToList()) {
    AssignmentAction(x);
}

...

public static void AssignmentAction(string x) {
    x = "WW";
}

This method won't modify x because of the pass-by-value semantics of C# and the immutability of strings.

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

9 Comments

Is a string array or in my case a List<byte[]> IEnumerable as I dont think it is?
@Jon: arrays and List<T> all support the IEnumerable interface
I don't think a Select() is what Jon wants, it sounds like he wants to modify the IEnumerable itself. Select will just give him the items that already are "WW"
@AgileJon: It sounds like you are confusing IEnumerable<T>.Where and IEnumerable<T>.Select.
@Downvoters: Two downvotes? Is this wrong? Is this harmful? What am I overlooking?
|
20

Other answers have explained why your current code doesn't work. Here's an extension method which would fix it:

// Must be in a static non-nested class
public static void ModifyEach<T>(this IList<T> source,
                                 Func<T,T> projection)
{
    for (int i = 0; i < source.Count; i++)
    {
        source[i] = projection(source[i]);
    }
}

Then use like this:

newsplit.ModifyEach(x => "WW");

That will work with any implementation of IList<T> such as arrays and List<T>. If you need it to work with an arbitrary IEnumerable<T> then you've got a problem, as the sequence itself may not be mutable.

Using Select() is a more functional approach of course, but sometimes mutating an existing collection is worth doing...

2 Comments

@Jon: I think you meant newsplit.ToList().ModifyEach(x => "WW") in your second code block?
@Jason: Nearly... but if you call ToList() but don't keep a reference to that new list, the modifications won't be preserved. In particular it would not modify the original collection. Have edited answer for intended use.
4

The ForEach will allow you to manipulate the elements of the IEnumerable, but not change the reference of the element.

ie, this would set a Foo property of each element in the IEnumerable to the string "WW":

newsplit.ToList().ForEach(x => x.Foo = "WW");

However, you won't be able to modify the values inside the IEnumerable itself.

1 Comment

At least in VB.Net I could only assign property of element inside ForEach if the multi-line ForEach, the Linq ForEach wouldn't assign by reference.
3

It's because the LINQ expression is creating Anonymous Types, and these are read-only. They can't be assigned to. Also, in a standard for each loop you cannot assign to a collection that is being enumerated. Try:

    foreach (var item in newsplit)
    {
        item = "this won't work";
    }

2 Comments

There is no anonymous type created in the given ForEach expression. The parameter is a delegate, which is applied to each element in the iteration; and of course, won't work for the reason you give.
You are right, of course - not sure what I was thinking! But the second part, about collections being immutable when iterated, is worth noting. Thanks.
1

You can write like this:

newsplit = newsplit.Select(x => "WW").ToList();

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.