Home All Groups Group Topic Archive Search About

Iterating over a list and altering it?

Author
5 Apr 2007 2:14 PM
Evan Reynolds
I am a C++ programmer and have been learning C#.  I have constructed a List<>
and I want to iterate over it, altering each string in the list.

In C++, I'd just create an iterator and walk the list, so I was looking for
something similar in C#.  I looked at foreach, List.ForEach, and IEnumerator.
The problem is that all of them seem to return readonly pointers to the
items in the List.  Therefore I can't alter the list.  So I'm using a for
loop, but ... I'm just baffled that there aren't any sort of iterators that
let me alter the list.  I have the feeling that there are, or that I'm just
not using these right.  So I figured I'd ask!

Here's some of the code I tried ... note that udLine is a struct, and udList
is a List of those structs.
           public List<udLine> udList = new List<udLine>(100);

           IEnumerator<udLine> enumerator = udList.GetEnumerator();
            while (enumerator.MoveNext())
            {
                enumerator.Current.header =
enumerator.Current.header.PadLeft(LongestHeaderLength);
            }


I really was curious to try this:
        public void AlterItem(udLine ud)
        {
            ud.header = ud.header.PadLeft(LongestHeaderLength);
        }

and then call:
            udList.ForEach(AlterItem);

but nothing happened.  My guess was that the ud was getting copied, so I
made it a ref argument:
        public void AlterItem(ref udLine ud)
        {
            ud.header = ud.header.PadLeft(LongestHeaderLength);
        }

         udList.ForEach(ref AlterItem);

but the compiler really didn't like that.

So I'm using a for loop.  But is there a way to make either of the above
statments work?

Thank you!

Author
5 Apr 2007 2:30 PM
Marc Gravell
OK - you started talking strings, and ended with structs... I think
you need to clarify:

are you a: trying to change the actual contents of the list (i.e. swap
items with replacements), or b: update properties of items that are in
the list?

if "b" then a foreach loop is fine, but the problem here may be that
you are using structures not classes; because these are value-typed
they are going to get copied left right and centre. First rule: .Net
structs should be immutable unless you really, really know what you
are doing. That means that you have to switch to scenario "a".

Now; for "a", note that changing the contents of the list (meaning:
adding, removing, swapping, etc) breaks iterators. This is
intentional. For this scenario the common solution is to use indexer
syntax instead:

for( int i = 0 ; i < list.count ; i++ ) {
  list[i] = theNewValuePerhapsFromTheOldValue();
}

Hope this helps,

Marc
Are all your drivers up to date? click for free checkup

Author
5 Apr 2007 3:07 PM
Chris Nahr
In addition to what Marc said, strings are _immutable_ reference type
in .NET so they behave like structs in terms of altering list elements
-- you have to reassign the element in question, ergo you can't use
iterators which are read-only with respect to the list.
Author
5 Apr 2007 3:54 PM
Evan Reynolds
Sorry, Marc - I started with a simplified question for clarity and then
forgot my simplification.  I have a list of structs.  Most of the structs are
strings, and I'm trying to alter one of the strings in each struct.

It sounds like I ended up with the right answer in the end, with the for
loop.  I take it that the ForEach concept is more to get the data out and put
it somewhere else than to operate on the data itself?
Author
5 Apr 2007 11:52 PM
Göran Andersson
Evan Reynolds wrote:
> Sorry, Marc - I started with a simplified question for clarity and then
> forgot my simplification.  I have a list of structs.  Most of the structs are
> strings, and I'm trying to alter one of the strings in each struct.
>
> It sounds like I ended up with the right answer in the end, with the for
> loop.  I take it that the ForEach concept is more to get the data out and put
> it somewhere else than to operate on the data itself?
>

Just make it a class instead of a struct, and you can change the members
of the class without problem.

--
Göran Andersson
_____
http://www.guffa.com
Author
5 Apr 2007 8:50 PM
Martin Z
Show quote Hide quote
On Apr 5, 10:30 am, "Marc Gravell" <marc.grav***@gmail.com> wrote:
> OK - you started talking strings, and ended with structs... I think
> you need to clarify:
>
> are you a: trying to change the actual contents of the list (i.e. swap
> items with replacements), or b: update properties of items that are in
> the list?
>
> if "b" then a foreach loop is fine, but the problem here may be that
> you are using structures not classes; because these are value-typed
> they are going to get copied left right and centre. First rule: .Net
> structs should be immutable unless you really, really know what you
> are doing. That means that you have to switch to scenario "a".
>
> Now; for "a", note that changing the contents of the list (meaning:
> adding, removing, swapping, etc) breaks iterators. This is
> intentional. For this scenario the common solution is to use indexer
> syntax instead:
>
> for( int i = 0 ; i < list.count ; i++ ) {
>   list[i] = theNewValuePerhapsFromTheOldValue();
>
> }
>
> Hope this helps,
>
> Marc

I have to disagree that structs should be immutable in .NET - most of
the internal structs provided by .NET are not immutable... in fact,
you'll find more classes are immutable than structs.  In general, the
approach is that, if you want a class that you can pass around to lots
of objects, it should be immutable - that avoids the terrifying
headache of aliasing (eg: fonts, strings, etc.).  So really, you have
it the other way around - if you want a class that will be used like a
struct, make it immutable.  Structs avoid aliasing automatically,
since they're always copy.  Personally, I think the important thing is
to always KNOW that a struct is a struct - if you have to include the
word "struct" in it's name, that's fine (since intellisense leaves out
this freakishly crucial piece of information).

Immutable structs defeat most of the advantages of using structs, and
the compiler is sufficiently smart to catch most of the stupid crap
you can get in trouble doing with a struct (IE changing values on a
temporary that will be dropped).   Besides, in most DotNet languages,
immutables are a headache to implement (sixteen billion hand-coded
constructors).
Author
6 Apr 2007 12:23 AM
Göran_Andersson
Martin Z wrote:
> I have to disagree that structs should be immutable in .NET  - most of
> the internal structs provided by .NET are not immutable... in fact,
> you'll find more classes are immutable than structs.

I haven't made a count so I can't say that you are wrong, but I find
that many of the most common structures are immutable, like for example
Color, DateTime, Decimal and TimeSpan. All built-in basic types, like
Int32 and Double, are of course also immutable.

I know of only a few that actually are mutable, like Point, Size and
Rect, and it has been widely debated if making them mutable was a good
move or not.

> In general, the
> approach is that, if you want a class that you can pass around to lots
> of objects, it should be immutable - that avoids the terrifying
> headache of aliasing (eg: fonts, strings, etc.).  So really, you have
> it the other way around - if you want a class that will be used like a
> struct, make it immutable.  Structs avoid aliasing automatically,
> since they're always copy.  Personally, I think the important thing is
> to always KNOW that a struct is a struct - if you have to include the
> word "struct" in it's name, that's fine (since intellisense leaves out
> this freakishly crucial piece of information).
>
> Immutable structs defeat most of the advantages of using structs,

Then I am not convinced that you are using structures the way that they
are intended.

If you are using structures as if they were classes, then it would of
course make no sense to make them immutable. On the other hand, in that
case it doesn't really make sense to make them structures either.

> and
> the compiler is sufficiently smart to catch most of the stupid crap
> you can get in trouble doing with a struct (IE changing values on a
> temporary that will be dropped).   Besides, in most DotNet languages,
> immutables are a headache to implement (sixteen billion hand-coded
> constructors).

I rarely find that a structure would need more than a few constructors.
With the limited size that is recommneded for structures, there can't be
so many different ways of creating them.

--
Göran Andersson
_____
http://www.guffa.com
Author
5 Apr 2007 9:29 PM
Bruce Wood
On Apr 5, 7:14 am, Evan Reynolds
<EvanReyno***@discussions.microsoft.com> wrote:
Show quoteHide quote
> I am a C++ programmer and have been learning C#.  I have constructed a List<>
> and I want to iterate over it, altering each string in the list.
>
> In C++, I'd just create an iterator and walk the list, so I was looking for
> something similar in C#.  I looked at foreach, List.ForEach, and IEnumerator.
>  The problem is that all of them seem to return readonly pointers to the
> items in the List.  Therefore I can't alter the list.  So I'm using a for
> loop, but ... I'm just baffled that there aren't any sort of iterators that
> let me alter the list.  I have the feeling that there are, or that I'm just
> not using these right.  So I figured I'd ask!
>
> Here's some of the code I tried ... note that udLine is a struct, and udList
> is a List of those structs.
>            public List<udLine> udList = new List<udLine>(100);
>
>            IEnumerator<udLine> enumerator = udList.GetEnumerator();
>             while (enumerator.MoveNext())
>             {
>                 enumerator.Current.header =
> enumerator.Current.header.PadLeft(LongestHeaderLength);
>             }
>
> I really was curious to try this:
>         public void AlterItem(udLine ud)
>         {
>             ud.header = ud.header.PadLeft(LongestHeaderLength);
>         }
>
> and then call:
>             udList.ForEach(AlterItem);
>
> but nothing happened.  My guess was that the ud was getting copied, so I
> made it a ref argument:
>         public void AlterItem(ref udLine ud)
>         {
>             ud.header = ud.header.PadLeft(LongestHeaderLength);
>         }
>
>          udList.ForEach(ref AlterItem);
>
> but the compiler really didn't like that.
>
> So I'm using a for loop.  But is there a way to make either of the above
> statments work?

Is there a particular reason why you chose to make udLine a struct
rather than a class? Note that these two keywords have much more
significant implications in C# than in C++.

I suggest that you make udLine a class. Then there will be no need to
pass it using "ref", and your problem should disppear.



Post Thread options