Modifying List items (generics)

  • Thread starter Thread starter Ivan Voras
  • Start date Start date
I

Ivan Voras

For a declaration like:

List<MyType> items = ...

where MyType is a struct, attempt to modify an item with

items[0].member = something;

fails with the message:

Cannot modify the return value of
'System.Collections.Generic.List<MyType>.this[int]' because it is not a
variable (CS1612) - E:\project\MainForm.cs:189,8

While searching the documentation I haven't found a specific mention
that it *should* work, but it seems logical. How to get around the
restriction?
 
Ivan said:
For a declaration like:

List<MyType> items = ...

where MyType is a struct, attempt to modify an item with

items[0].member = something;

fails with the message:

Cannot modify the return value of
'System.Collections.Generic.List<MyType>.this[int]' because it is not a
variable (CS1612) - E:\project\MainForm.cs:189,8

While searching the documentation I haven't found a specific mention
that it *should* work, but it seems logical. How to get around the
restriction?

How wise of the compiler to prevent you from doing that. If it would
have let you, you would have changed the member of the copy of the value
that you requested from the list. This would have no effect at all, as
the original value in the list would be unchanged.

If you want to change the member of a struct, you have to get a copy of
the value, change the member, and replace the original value with the
changed value:

MyType temp = items[0];
temp.member = something;
items[0] = temp;

However, a struct should generally be immutable. If you have items that
you want to modify, you should make it a class instead.
 
Göran Andersson said:
MyType temp = items[0];
temp.member = something;
items[0] = temp;

However, a struct should generally be immutable. If you have items that
you want to modify, you should make it a class instead.

Hmm, this reduces the usability of structs. But ok, it's probably
because [] is overloaded and returns a struct (i.e. a value) and
pointers don't exist.
 
Ivan said:
Göran Andersson said:
MyType temp = items[0];
temp.member = something;
items[0] = temp;

However, a struct should generally be immutable. If you have items that
you want to modify, you should make it a class instead.

Hmm, this reduces the usability of structs. But ok, it's probably
because [] is overloaded and returns a struct (i.e. a value) and
pointers don't exist.

Well, the indexer is actually not overloaded, it always returns the
value of the item. For a reference type the value is the reference, and
for value types it's the value itself. :)
 
Ivan said:
For a declaration like:
List<MyType> items = ...
where MyType is a struct, attempt to modify an item with
items[0].member = something;
fails with the message:
Cannot modify the return value of
'System.Collections.Generic.List<MyType>.this[int]' because it is not a
variable (CS1612) - E:\project\MainForm.cs:189,8
While searching the documentation I haven't found a specific mention
that it *should* work, but it seems logical. How to get around the
restriction?

How wise of the compiler to prevent you from doing that. If it would
have let you, you would have changed the member of the copy of the value
that you requested from the list. This would have no effect at all, as
the original value in the list would be unchanged.

If you want to change the member of a struct, you have to get a copy of
the value, change the member, and replace the original value with the
changed value:

MyType temp = items[0];
temp.member = something;
items[0] = temp;

However, a struct should generally be immutable. If you have items that
you want to modify, you should make it a class instead.

Sorry, probably slightly off topic, but I don't consider that the best
advice. Certainly you should think long and hard before creating a
mutable struct, precisely because of the behaviour the OP just
observed: they act in ways that aren't intuitive to the casual C#
programmer.

I would be more inclined to say the following: "If you have items that
logically should be structs, but you want to be able to modify them,
then realize that allowing structs to be modified in place is just
syntactic sugar for adding another constructor. Consider doing that
instead."

Viz:

structValue.Member = newValue;

is equivalent to:

structValue = new MyStruct(structValue, newValue);

A bit more typing, perhaps, and a few more bytes get moved around, but
nothing earthshaking.

Some examples of mutable structs in the .NET Framework (which cause no
end of consternation if this newsgroup is anything to go by) are Point
and Rectangle. The posts asking why you can't do this:

myForm.Location.X = 5;

seem to have died down lately, but I'm waiting for them to flare up
again.

Anyway, I don't consider it a good idea to decide between using struct
and class based on whether you think the thing should be mutable. IMHO
it has much more to do with semantics, in particular what I refer to
as "whether the thing has an identity."
 
Bruce said:
Ivan said:
For a declaration like:
List<MyType> items = ...
where MyType is a struct, attempt to modify an item with
items[0].member = something;
fails with the message:
Cannot modify the return value of
'System.Collections.Generic.List<MyType>.this[int]' because it is not a
variable (CS1612) - E:\project\MainForm.cs:189,8
While searching the documentation I haven't found a specific mention
that it *should* work, but it seems logical. How to get around the
restriction?
How wise of the compiler to prevent you from doing that. If it would
have let you, you would have changed the member of the copy of the value
that you requested from the list. This would have no effect at all, as
the original value in the list would be unchanged.

If you want to change the member of a struct, you have to get a copy of
the value, change the member, and replace the original value with the
changed value:

MyType temp = items[0];
temp.member = something;
items[0] = temp;

However, a struct should generally be immutable. If you have items that
you want to modify, you should make it a class instead.

Sorry, probably slightly off topic, but I don't consider that the best
advice.

That depends on how deep you want to go into the matter. I think that
it's good advice for anyone who doesn't really know the difference
between a struct and a class.

Perhaps I should have added a "in most cases" in the advice about making
it a class. That would suggest that there is more to learn about the
matter to be able to make a definitive decision in every case.
Certainly you should think long and hard before creating a
mutable struct, precisely because of the behaviour the OP just
observed: they act in ways that aren't intuitive to the casual C#
programmer.

I would be more inclined to say the following: "If you have items that
logically should be structs, but you want to be able to modify them,
then realize that allowing structs to be modified in place is just
syntactic sugar for adding another constructor. Consider doing that
instead."

I don't think that this really contradicts my advice. Most items that
you want to modify shouldn't logically be structures anyway.

However, the part about modifying structures being syntactic sugar for a
constructor only applies if you modify the entire value of the
structure, and in that case it should always be done using a constructor.
Viz:

structValue.Member = newValue;

is equivalent to:

structValue = new MyStruct(structValue, newValue);

A bit more typing, perhaps, and a few more bytes get moved around, but
nothing earthshaking.

Especially if the suggested size limit for structures of 16 bytes is
followed. :)
Some examples of mutable structs in the .NET Framework (which cause no
end of consternation if this newsgroup is anything to go by) are Point
and Rectangle. The posts asking why you can't do this:

myForm.Location.X = 5;

seem to have died down lately, but I'm waiting for them to flare up
again.

They surely will. Making a structure mutable will always cause problems
for people unaware of the problems that it introduces.
Anyway, I don't consider it a good idea to decide between using struct
and class based on whether you think the thing should be mutable. IMHO
it has much more to do with semantics, in particular what I refer to
as "whether the thing has an identity."

Good advice. I guess that the basis for the decision depends on how you
are accustomed to think about data.
 
Bruce said:
Sorry, probably slightly off topic, but I don't consider that the best
advice. Certainly you should think long and hard before creating a
mutable struct, precisely because of the behaviour the OP just
observed: they act in ways that aren't intuitive to the casual C#
programmer.

....or to programmers coming from other languages...
I would be more inclined to say the following: "If you have items that
logically should be structs, but you want to be able to modify them,
then realize that allowing structs to be modified in place is just
syntactic sugar for adding another constructor. Consider doing that
instead."

While I accept there are situation that benefit from unmutable structs,
they are few and far between. So, for now, since I'm thinking in terms
of mutable structures, I'll just make them classes.
Some examples of mutable structs in the .NET Framework (which cause no
end of consternation if this newsgroup is anything to go by) are Point
and Rectangle. The posts asking why you can't do this:

myForm.Location.X = 5;

seem to have died down lately, but I'm waiting for them to flare up
again.

.... because you must admit that doing it that way looks very
straightforward :)
Anyway, I don't consider it a good idea to decide between using struct
and class based on whether you think the thing should be mutable. IMHO
it has much more to do with semantics, in particular what I refer to
as "whether the thing has an identity."

Maybe on a higher level of thinking about it, yes, but C# is still a
more down-to-earth language. I was very surprised with the decision to
make "structs" have methods, constructors, etc, instead of making them
just be light-weight containers for data like they're mostly used (in
other languages).
 
Back
Top