List<> of struct with property. Cannot change value of property. why?

Z

Zytan

This returns the following error:
"Cannot modify the return value of
'System.Collections.Generic.List<MyStruct>.this[int]' because it is
not a variable"
and I have no idea why! Do lists return copies of their elements?
Why can't I change the element itself?

class Program
{
private struct MyStruct
{
private int myVar;
public int MyProperty
{
get { return myVar; }
set { myVar = value; }
}
}

private static List<MyStruct> list = new List<MyStruct>();

private static void Main(string[] args)
{
MyStruct x = new MyStruct();
x.MyProperty = 45;
list.Add(x);
list[0].MyProperty = 45; // <----------- ERROR HERE
}
}

Zytan
 
B

Bruce Wood

This returns the following error:
"Cannot modify the return value of
'System.Collections.Generic.List<MyStruct>.this[int]' because it is
not a variable"
and I have no idea why! Do lists return copies of their elements?

Yes. The [] operator on a list is, in fact, a function, so the value
stored at that location in the list is returned as a function result,
on the stack.

This doesn't cause problems for reference types, because usually you
want to change some property of the reference type, so the fact that
you get a copy of the reference in the list (not the actual reference
that is in the list) doesn't cause problems.

However, for value types exactly the same thing happens, and it does
cause problems: the value is copied from the list onto the stack and
returned as a function result. Modifying the returned value, of
course, has no effect on the contents of the list. The compiler wisely
catches this.
Why can't I change the element itself?

class Program
{
private struct MyStruct
{
private int myVar;
public int MyProperty
{
get { return myVar; }
set { myVar = value; }
}
}

private static List<MyStruct> list = new List<MyStruct>();

private static void Main(string[] args)
{
MyStruct x = new MyStruct();
x.MyProperty = 45;
list.Add(x);
list[0].MyProperty = 45; // <----------- ERROR HERE
}
}

You need to do this:

MyStruct y = list[0];
y.MyProperty = 45;
list[0] = y;
 
Z

Zytan

Yes. The [] operator on a list is, in fact, a function, so the value
stored at that location in the list is returned as a function result,
on the stack.
Ok.

This doesn't cause problems for reference types, because usually you
want to change some property of the reference type, so the fact that
you get a copy of the reference in the list (not the actual reference
that is in the list) doesn't cause problems.

Right, so that's why it's normally not an issue.
However, for value types exactly the same thing happens, and it does
cause problems: the value is copied from the list onto the stack and
returned as a function result. Modifying the returned value, of
course, has no effect on the contents of the list. The compiler wisely
catches this.

Yup, and structs are value types, so this makes sense.
You need to do this:

MyStruct y = list[0];
y.MyProperty = 45;
list[0] = y;

Ok, thanks, Bruce!! This helps a lot!

Zytan
 
Z

Zytan

The compiler wisely
catches this.

NOTE, the compiler does NOT catch everything! The following code
escapes detection:

MyStruct x = new MyStruct();
list[index].MethodThatChangesStructsFields(x);

I guess this could be shortened to:

list[index].MethodThatChangesStructsFields();

And likely THAT escapes detection, as well. Damn! I've been changing
all my ararys that are created dynamically (length unknown) into
List<>, to avoid Array.Resize.

Zytan
 
Z

Zytan

I guess this could be shortened to:
list[index].MethodThatChangesStructsFields();

Confirmed. This operates on the COPY, and any changes are lost, and
the compiler does NOT warn about this.

Zytan
 
P

Peter Duniho

[...]
I guess this could be shortened to:

list[index].MethodThatChangesStructsFields();

And likely THAT escapes detection, as well. Damn! I've been changing
all my ararys that are created dynamically (length unknown) into
List<>, to avoid Array.Resize.

Well, you can still apply the same technique Bruce mentions:

MyStruct x = list[index];

x.MethodThatChangesStructsFields();
list[index] = x;

Pete
 
J

Jon Skeet [C# MVP]

Zytan said:
I guess this could be shortened to:

list[index].MethodThatChangesStructsFields();

Confirmed. This operates on the COPY, and any changes are lost, and
the compiler does NOT warn about this.

No, and there's no way it could. There's nothing available to the
compiler to let it know that the method changes the contents of the
struct.

(Remember when we warned you about making mutable structs, back in
March? This is just one of the problems. Just say no to mutable
structs, basically :)
 
S

Samuel R. Neff

If you declare your structure to implement an interface and then
declare the list as containing the interface instead of the structure,
then you can work with the structure (through the interface
implemented members) directly (interfaces allow you to pierce the
box).

Code sample below.


HTH,

Sam


------------------------------------------------------------
We're hiring! B-Line Medical is seeking .NET
Developers for exciting positions in medical product
development in MD/DC. Work with a variety of technologies
in a relaxed team environment. See ads on Dice.com.


public static void Test()
{
MyClass[] classArray = new MyClass[1];
MyStruct[] structArray = new MyStruct[1];
MyInterface[] interfaceArray = new MyInterface[1];
List<MyClass> classList = new List<MyClass>(1);
List<MyStruct> structList = new List<MyStruct>(1);
List<MyInterface> interfaceList = new List<MyInterface>(1);

classArray[0] = new MyClass();
classList.Add(new MyClass());
interfaceArray[0] = new MyStruct();
structList.Add(new MyStruct());
interfaceList.Add(new MyStruct());

classArray[0].X++;
interfaceArray[0].X++;
structArray[0].X++;
classList[0].X++;

// Cannot modify the return value of 'List<MyStruct>.this[int]'
// because it is not a variable
//structList[0].X++;
interfaceList[0].X++;

Console.WriteLine("Class Array : " + classArray[0].X);
Console.WriteLine("Struct Array : " + structArray[0].X);
Console.WriteLine("Interface Array : " + interfaceArray[0].X);
Console.WriteLine("Class List : " + classList[0].X);
Console.WriteLine("Struct List : " + structList[0].X);
Console.WriteLine("Interface List : " + interfaceList[0].X);
}
}

public class MyClass : MyInterface
{
private int x;

public int X
{
get
{
return x;
}
set
{
x = value;
}
}
}

public struct MyStruct : MyInterface
{
private int x;

public int X
{
get
{
return x;
}
set
{
x = value;
}
}
}

public interface MyInterface
{
int X { get; set; }
}
 
B

Bruce Wood

If you declare your structure to implement an interface and then
declare the list as containing the interface instead of the structure,
then you can work with the structure (through the interface
implemented members) directly (interfaces allow you to pierce the
box).

Code sample below.

HTH,

Sam

------------------------------------------------------------
We're hiring! B-Line Medical is seeking .NET
Developers for exciting positions in medical product
development in MD/DC. Work with a variety of technologies
in a relaxed team environment. See ads on Dice.com.

public static void Test()
{
MyClass[] classArray = new MyClass[1];
MyStruct[] structArray = new MyStruct[1];
MyInterface[] interfaceArray = new MyInterface[1];
List<MyClass> classList = new List<MyClass>(1);
List<MyStruct> structList = new List<MyStruct>(1);
List<MyInterface> interfaceList = new List<MyInterface>(1);

classArray[0] = new MyClass();
classList.Add(new MyClass());
interfaceArray[0] = new MyStruct();
structList.Add(new MyStruct());
interfaceList.Add(new MyStruct());

classArray[0].X++;
interfaceArray[0].X++;
structArray[0].X++;
classList[0].X++;

// Cannot modify the return value of 'List<MyStruct>.this[int]'
// because it is not a variable
//structList[0].X++;
interfaceList[0].X++;

Console.WriteLine("Class Array : " + classArray[0].X);
Console.WriteLine("Struct Array : " + structArray[0].X);
Console.WriteLine("Interface Array : " + interfaceArray[0].X);
Console.WriteLine("Class List : " + classList[0].X);
Console.WriteLine("Struct List : " + structList[0].X);
Console.WriteLine("Interface List : " + interfaceList[0].X);
}

}

public class MyClass : MyInterface
{
private int x;

public int X
{
get
{
return x;
}
set
{
x = value;
}
}

}

public struct MyStruct : MyInterface
{
private int x;

public int X
{
get
{
return x;
}
set
{
x = value;
}
}

}

public interface MyInterface
{
int X { get; set; }

}

Now I'm curious: does the array MyInterface[] contain the actual
values, or does one incur boxing overhead when putting the value into
the array? In other words, is it an array of MyStruct, or an array of
boxes of MyStructs?
 
J

Jon Skeet [C# MVP]

Bruce Wood said:
Now I'm curious: does the array MyInterface[] contain the actual
values, or does one incur boxing overhead when putting the value into
the array? In other words, is it an array of MyStruct, or an array of
boxes of MyStructs?

Boxes - the values are references, and have to be, given that you could
use a reference type implementation.
 
S

Samuel R. Neff

They are boxes, but it's not 100% the same as boxing in other
situations. Because of the interface they are boxed once and can
therefore be manipulated within the box, so you can do things to them
without unboxing. So you get boxing overhead possibly without
unboxing overhead.

For code clarity and consistency I would lean towards using immutable
structures, but it's an interesting technical clarification. :)

Sam

------------------------------------------------------------
We're hiring! B-Line Medical is seeking .NET
Developers for exciting positions in medical product
development in MD/DC. Work with a variety of technologies
in a relaxed team environment. See ads on Dice.com.



If you declare your structure to implement an interface and then
declare the list as containing the interface instead of the structure,
then you can work with the structure (through the interface
implemented members) directly (interfaces allow you to pierce the
box).

Code sample below.

HTH,

Sam
....

Now I'm curious: does the array MyInterface[] contain the actual
values, or does one incur boxing overhead when putting the value into
the array? In other words, is it an array of MyStruct, or an array of
boxes of MyStructs?
 
J

Jon Skeet [C# MVP]

Samuel R. Neff said:
They are boxes, but it's not 100% the same as boxing in other
situations. Because of the interface they are boxed once and can
therefore be manipulated within the box, so you can do things to them
without unboxing. So you get boxing overhead possibly without
unboxing overhead.

It's worth clarifying here that the boxing itself is exactly the same
as normal. Indeed, you can take something that has been boxed in a
different way and cast it to the interface:

object o = 5; // Normal boxing
IComparable c = (IComparable)o; // Normal reference cast
For code clarity and consistency I would lean towards using immutable
structures, but it's an interesting technical clarification. :)

Agreed :)
 
Z

Zytan

No, and there's no way it could. There's nothing available to the
compiler to let it know that the method changes the contents of the
struct.

Yes, it could. The compiler knows, at least to some degree, that a
method changes the struct that owns it. This is a similar issue to
C#'s lack of 'const' for variables and methods. If it had the
internal features that checked such things, then it'd know. Then, it
could warn that you are changing a struct from a method marked as
'const' (which guarantees to do no such thing), and it could fire a
warning for the above case of modifying a temporary.
(Remember when we warned you about making mutable structs, back in
March? This is just one of the problems. Just say no to mutable
structs, basically :)

Yup! :) But, it just doesn't seem fair, since the language isn't
quite 'complete', yet. I never had troubles with Lists in C++ like
this. But, it's becoming obvious that the c# designers just want us
to use reference types when we can, and everything in the langauge is
based on this, and thus, it is wildly different than C++ in that
regard. I guess there's good and bad that comes with that.

Zytan
 
P

Peter Duniho

Yes, it could. The compiler knows, at least to some degree, that a
method changes the struct that owns it.

I'm not sure why you say that. As it's compiling the method, it's true
that information is present. But outside of that case, how would the
compiler know the method changes the struct? Should it flag all methods
that change a struct somehow, and cache that information somewhere for use
when compiling other code? Should the linker be required to carry this
flag around too, so that when you import a reference to a struct type not
compiled with the current project, you still have that information?
This is a similar issue to
C#'s lack of 'const' for variables and methods. If it had the
internal features that checked such things, then it'd know.

I don't see it as "similar". I see it as identical. :)

If C# had "const" for variables and methods, then it would also have the
function name decoration that's used to convey that information that
C++ has. Then when compiling code that uses a method, *if* the "const"
keyword were used, it could know that the method doesn't change the struct.

But then what is the compiler supposed to do with methods not marked as
"const"? Should it automatically assume that the method *does* change the
struct? And in that case, should the compiler instead of copying the
value type create some sort of hidden reference to it on which the method
can operate? And if so, why shouldn't the compiler just always do that?
[...]
(Remember when we warned you about making mutable structs, back in
March? This is just one of the problems. Just say no to mutable
structs, basically :)

Yup! :) But, it just doesn't seem fair, since the language isn't
quite 'complete', yet.

IMHO, C# is only "not quite complete" in the same way that C++ and BASIC
and FORTRAN are all "not quite complete".

More specifically, it seems to me that C# has pretty well decided what
value types are going to do and not do, and it's pretty clear that you
don't get to have references to value types. Given the lack of references
I never had troubles with Lists in C++ like
this.

You would have if there were any implementations of lists in C++ that
provided access to list items by value. There may in fact be such an
implementation, and if there is, it will have the same issue. It will
return a copy of a value within the list, and you can operate on that copy
'till the cows come home and the item in the list will never change.
But, it's becoming obvious that the c# designers just want us
to use reference types when we can, and everything in the langauge is
based on this, and thus, it is wildly different than C++ in that
regard. I guess there's good and bad that comes with that.

I wouldn't agree that the designers want you to use reference types when
you can. There's a time and place for a value type, and in fact they even
have their place in lists. But it seems to me that other than some
semantic oddities (that I've complained about myself :) ), the basic
behavior is well-defined.

I agree that this particular example is somewhat confusing. In the same
way that overloading the "new" operator bugs me about reference types
versus value types, you're dealing with a situation in which the indexing
operator "[]" behaves differently depending on whether you've got an array
or a List<>. With an array, you get the actual item in the array. With a
List<> you get the value from the array, which is always a copy of the
item in the array (and if it's a copy of the reference then you can still
directly act upon the instance referenced). But once you understand this
difference, the underyling "reference vs value" behavior is consistent and
reliable.

Pete
 
Z

Zytan

Yes, it could. The compiler knows, at least to some degree, that a
I'm not sure why you say that. As it's compiling the method, it's true
that information is present. But outside of that case, how would the
compiler know the method changes the struct?

The same way C++ deals with const methods.
Should it flag all methods
that change a struct somehow, and cache that information somewhere for use
when compiling other code?

Yes, but it would seem less silly if there were more reasons to use
such information. And having "const" is a large one.
Should the linker be required to carry this
flag around too, so that when you import a reference to a struct type not
compiled with the current project, you still have that information?

I have no idea, but I would assume that a C++ linker does. After all,
it's just one bit.
I don't see it as "similar". I see it as identical. :)
:)

If C# had "const" for variables and methods, then it would also have the
function name decoration that's used to convey that information that
C++ has. Then when compiling code that uses a method, *if* the "const"
keyword were used, it could know that the method doesn't change the struct.
Yup.

But then what is the compiler supposed to do with methods not marked as
"const"? Should it automatically assume that the method *does* change the
struct?

In c++, if you have a method that doesn't change it, but you don't
tell it specifically that it doesn't change it, then the compiler
assumes that it COULD change it, and thus doesn't trust that it's
being nice. So, it's not that it assumes it does change it, it just
assumes that it could change it. And if that's an issue, which it is
for a method called on a temporary copy (are these called
'temporaries'?), then the compiler could say, hey, since you didn't
say you won't modify this struct, it means you may modify it, and if
you do, you're not going to get what you expect. Just a friendly
warning.
And in that case, should the compiler instead of copying the
value type create some sort of hidden reference to it on which the method
can operate? And if so, why shouldn't the compiler just always do that?

No, it shouldn't do your work for you. It's just there to notify you
when you're being stupid.
IMHO, C# is only "not quite complete" in the same way that C++ and BASIC
and FORTRAN are all "not quite complete".

I agree.
More specifically, it seems to me that C# has pretty well decided what
value types are going to do and not do, and it's pretty clear that you
don't get to have references to value types. Given the lack of references
to value types, I don't see any clean way to have a List<> return a
reference to a value type within the list for some method to operate on.

I am still programming in C++ mode. It's taking me a while to realize
that structs become more useless when the language is built mainly
around reference types.
You would have if there were any implementations of lists in C++ that
provided access to list items by value. There may in fact be such an
implementation, and if there is, it will have the same issue. It will
return a copy of a value within the list, and you can operate on that copy
'till the cows come home and the item in the list will never change.

I was referring to the STL, which is part of the (or at least the
defacto) standard. It was put together surprisingly well.
I wouldn't agree that the designers want you to use reference types when
you can. There's a time and place for a value type, and in fact they even
have their place in lists. But it seems to me that other than some
semantic oddities (that I've complained about myself :) ), the basic
behavior is well-defined.

I guess at the moment I decided to have a method in my struct, I
should have moved to a class.
I agree that this particular example is somewhat confusing. In the same
way that overloading the "new" operator bugs me about reference types
versus value types, you're dealing with a situation in which the indexing
operator "[]" behaves differently depending on whether you've got an array
or a List<>. With an array, you get the actual item in the array. With a
List<> you get the value from the array, which is always a copy of the
item in the array (and if it's a copy of the reference then you can still
directly act upon the instance referenced). But once you understand this
difference, the underyling "reference vs value" behavior is consistent and
reliable.

Yup. thanks for your comments, Pete

Zytan
 
J

Jon Skeet [C# MVP]

Zytan said:
Yes, it could. The compiler knows, at least to some degree, that a
method changes the struct that owns it.

Well, the compiler certainly doesn't know it from the metadata about
the method. Are you suggesting that the compiler should start looking
at the *implementation* of the method (which could be in a different
assembly) to work out what to do? What if the implementation changes?
This is a similar issue to
C#'s lack of 'const' for variables and methods. If it had the
internal features that checked such things, then it'd know.

And then that would show up in the method metadata, yes - but as it is,
that information isn't there.
Then, it could warn that you are changing a struct from a method
marked as 'const' (which guarantees to do no such thing), and it
could fire a warning for the above case of modifying a temporary.

Again, if "const" were available, that would make a lot of sense - but
it isn't, for better or worse.
Yup! :) But, it just doesn't seem fair, since the language isn't
quite 'complete', yet. I never had troubles with Lists in C++ like
this. But, it's becoming obvious that the c# designers just want us
to use reference types when we can, and everything in the langauge is
based on this, and thus, it is wildly different than C++ in that
regard. I guess there's good and bad that comes with that.

It's wildly different from C++ in many ways. I think those who haven't
used C++ to start with actually have an advantage when learning C#, in
terms of not having to "unlearn" things.
 
J

Jon Skeet [C# MVP]

I guess at the moment I decided to have a method in my struct, I
should have moved to a class.

No - there are plenty of times it makes sense to have methods in
structs. Look at DateTime - loads of methods in there, and it's still a
perfectly good value type. It just doesn't have any methods which
mutate it.
 
J

Jon Skeet [C# MVP]

I agree that this particular example is somewhat confusing. In the same
way that overloading the "new" operator bugs me about reference types
versus value types, you're dealing with a situation in which the indexing
operator "[]" behaves differently depending on whether you've got an array
or a List<>. With an array, you get the actual item in the array.

Or to be specific (and odd!) you get the *variable* in the array.
That's the weird bit - that an array isn't a collection of values, it's
a collection of variables (in the spec, anyway). So

int[] n = new int[4];
is sort of equivalent to:

int n0 = 0;
int n1 = 0;
int n2 = 0;
int n3 = 0;

(except sized at runtime, of course).

That's why you can pass array elements by reference, too. Conceptually
slightly odd, IMO.
 
Z

Zytan

No - there are plenty of times it makes sense to have methods in
structs. Look at DateTime - loads of methods in there, and it's still a
perfectly good value type. It just doesn't have any methods which
mutate it.

Right. So. Structs should be immutable. That's the golden rule?
All fields should be private (or readonly) with the constructor
setting them?

Zytan
 
J

Jon Skeet [C# MVP]

Zytan said:
Right. So. Structs should be immutable. That's the golden rule?

It's at least a silver rule. There may be good reasons to have
genuinely mutable structs in very special cases, but I haven't seen
them yet.
All fields should be private (or readonly) with the constructor
setting them?

Fields should be private anyway, but yes, they should be private and
probably all readonly, unless you want to set them from private methods
called in the constructor.

Of course, you might want to have mutable fields for caching purposes
(e.g. caching the result of calling GetHashcode) which don't change the
perceivable state - no harm in that, although it would be unusual.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top