Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / May 2007

Tip: Looking for answers? Try searching our database.

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

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Zytan - 14 May 2007 18:28 GMT
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
Bruce Wood - 14 May 2007 18:46 GMT
> 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?
>
[quoted text clipped - 20 lines]
>         }
>     }

You need to do this:

MyStruct y = list[0];
y.MyProperty = 45;
list[0] = y;
Zytan - 14 May 2007 18:57 GMT
> 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
Zytan - 14 May 2007 19:38 GMT
> 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
Zytan - 14 May 2007 19:43 GMT
> 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
Jon Skeet [C# MVP] - 14 May 2007 19:55 GMT
> > 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 :)

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Zytan - 14 May 2007 22:44 GMT
> 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
Peter Duniho - 14 May 2007 23:11 GMT
>> 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.

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
[quoted text clipped - 3 lines]
> 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  
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 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
Zytan - 14 May 2007 23:23 GMT
> > 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?

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.

> > 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.

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.

> > I never had troubles with Lists in C++ like
> > this.
[quoted text clipped - 4 lines]
> 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  
[quoted text clipped - 6 lines]
> difference, the underyling "reference vs value" behavior is consistent and  
> reliable.

Yup.  thanks for your comments, Pete

Zytan
Jon Skeet [C# MVP] - 14 May 2007 23:44 GMT
<snip>

> > 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  
[quoted text clipped - 4 lines]
> 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.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Zytan - 14 May 2007 23:51 GMT
> 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
Jon Skeet [C# MVP] - 15 May 2007 00:02 GMT
> > 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?

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.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Jon Skeet [C# MVP] - 14 May 2007 23:47 GMT
<snip>

> 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.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Jon Skeet [C# MVP] - 14 May 2007 23:42 GMT
> > 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.

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.

> > (Remember when we warned you about making mutable structs, back in
> > March? This is just one of the problems. Just say no to mutable
[quoted text clipped - 6 lines]
> 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.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Christof Nordiek - 15 May 2007 11:52 GMT
>> The compiler knows, at least to some degree, that a
>> method changes the struct that owns it.
[quoted text clipped - 3 lines]
> at the *implementation* of the method (which could be in a different
> assembly) to work out what to do? What if the implementation changes?

If C# had const methods similar to C++, then this could be stored in the
metadata. And the compiler could check this while compiling the method. But
that certainly would be problematic if different languages are used. Other
languages should use the same attribute, and methods from languages, that
don't use that same attribute would be regarded as possibly changing the
struct. This problem I suppose, doesn't exsist in C++, does it?

BTW How does C++ handle const methods from other modules?

Christof
Peter Duniho - 15 May 2007 18:07 GMT
> [...]
> BTW How does C++ handle const methods from other modules?

As far as I can recall, C++ does not use the "const" information in the  
way that Zytan is asking for C# to.  That is, the "const" attribute of a  
method or parameters doesn't affect how an item is retrieved from a data  
structure or how a function is called.  It only controls whether the  
compiler can assume, for the purpose of evaluating "const"-ness in the  
code being compiled, that the called function doesn't modify the data.

That is, the main reason it's useful in external modules is so that your  
own modules can use "const" too.  :)

If C# *did* have the "const" keyword, I would expect it to work more like  
that, than in the way that Zytan would like it to work.

Pete
Zytan - 15 May 2007 22:20 GMT
> As far as I can recall, C++ does not use the "const" information in the  
> way that Zytan is asking for C# to.  That is, the "const" attribute of a  
[quoted text clipped - 8 lines]
> If C# *did* have the "const" keyword, I would expect it to work more like  
> that, than in the way that Zytan would like it to work.

I think I am saying the same thing.

Once you have this boolean value that states the "const"-ness of a
method, then in the case of calling a non-const method on a List<>
element that is a temporary (the compiler already knows when it's a
temporary), the compiler could complain and say: "Hey, you might be
modifying a temporary!  You should only call const-methods on
temporaries!"

Zytan
Peter Duniho - 15 May 2007 22:45 GMT
>> If C# *did* have the "const" keyword, I would expect it to work more  
>> like that, than in the way that Zytan would like it to work.
>
> I think I am saying the same thing.

Sorry...I guess I misunderstood what you wanted the language to do.

> Once you have this boolean value that states the "const"-ness of a
> method, then in the case of calling a non-const method on a List<>
> element that is a temporary (the compiler already knows when it's a
> temporary), the compiler could complain and say: "Hey, you might be
> modifying a temporary!  You should only call const-methods on
> temporaries!"

Well, IMHO the compiler could easily provide the same warning today,  
without the "const" keyword.

After all, in C++ there's a LOT of code that doesn't modify data that  
isn't marked "const".  Relying on the "const" keyword to enable a warning  
wouldn't have been a good idea in C++, because you'd get a lot of false  
positives due to the large amount of code that is "const" without using  
"const".

Conversely, if such a warning is a good idea (in C# or C++), it seems to  
me that the compiler ought to just warn and forget about trying to  
determine whether the method is actually a "const" method or not.

Personall, because of the false positive issue, I think such a warning  
isn't a good idea.  You'd see it far too often when it wasn't legitimate  
for it to actually be useful.

Now, I suppose the language could add a feature in which it requires the  
compiler to analyze every method and apply its own "const" attribute based  
on that analysis.  But that opens a whole new can of worms, including what  
is essentially the same problem that the C++ "const" keyword had: until  
every single function in the call chain supports that attribute, you wind  
up with a lot of functions that can't be marked as "const" even though  
they really are (or conversely, you have to do casting a bunch of stuff to  
glue the const/not-const stuff together).

My feeling is that while it's possible theoretically to address the issue,  
I think it's simpler to just make the language simple and consistent, and  
require developers to understand that the List<> "[]" operator is  
providing a copy of the element in the list.  There's too many situations  
where working directly with the copied value is useful for the compiler to  
go around warning you every time you do it, and introducing a "const"  
attribute (explicit or implicit) creates far too many new hassles to make  
it worth the trouble.

Opinions will vary, of course.  :)

Pete
Ben Voigt - 15 May 2007 23:07 GMT
> After all, in C++ there's a LOT of code that doesn't modify data that
> isn't marked "const".  Relying on the "const" keyword to enable a warning
> wouldn't have been a good idea in C++, because you'd get a lot of false
> positives due to the large amount of code that is "const" without using
> "const".

Actually, in a good programmer's hands, that's a notice to the compiler that
the function is not guaranteed to leave the data unchanged.  I'd hate for
the compiler to analyze a stub function, make an automatic determination of
const-ness, and somehow affect how the code around it is error-checked.
(Note that optimizing the generated machine code is ok, optimizing away
errors and warnings is not).

Understanding and using const-correctness is a prerequisite for being a
professional C++ programmer.  It's an important part of documentation,
compile-time error checking, and enables automatic optimizations that
couldn't otherwise be performed.
Peter Duniho - 15 May 2007 23:21 GMT
> [...]
> Understanding and using const-correctness is a prerequisite for being a
> professional C++ programmer.

I'll buy "understanding".  I won't accept "using".  Too much  
professionally-written C++ code (including some of my own) simply does not  
use "const" for you to make that claim.
Ben Voigt - 16 May 2007 00:37 GMT
>> [...]
>> Understanding and using const-correctness is a prerequisite for being a
[quoted text clipped - 3 lines]
> professionally-written C++ code (including some of my own) simply does not
> use "const" for you to make that claim.

And we're already waaaaay off-topic for the C# group.

I'll just say that you'd have to have very specialized code for const not to
be useful.  Subsets of C++ used by embedded compilers, perhaps (although in
my experience they overload const and make it mandatory).  But string
literals are arrays of const char, so it's *really* hard to do anything
useful without const and maintain any semblance of efficiency.
Peter Duniho - 16 May 2007 08:33 GMT
> And we're already waaaaay off-topic for the C# group.

No doubt.  It happens.  :)  At least we're still talking about  
programming.  :)

> I'll just say that you'd have to have very specialized code for const  
> not to be useful.

"useful" != "mandatory"

> [...]  But string
> literals are arrays of const char, so it's *really* hard to do anything
> useful without const and maintain any semblance of efficiency.

Are you saying that failing to use the "const" keyword in C++ causes  
performance problems?  That's an interesting statement.  Care to elaborate?

Pete
Ben Voigt - 16 May 2007 14:38 GMT
>Are you saying that failing to use the "const" keyword in C++ causes
>performance problems?  That's an interesting statement.  Care to elaborate?

Well, for the specific case of a string literal, you can do:

const char* psz = "This is a constant string";

or

char[] asz = "This is a non-const literal string";

The second form requires the compiler to actually initialize the memory on
every entry to the function, instead of referencing a string literal.  It
also prevents string folding, which increases the size of the binary, which
has its own set of performance problems.

Beyond that, pass-by-const reference is more performant than pass-by-value
for large data types.  Sure, you don't strictly need const for that, you can
pass by reference and get most of the performance gains.  But, const also
enables a lot of optimizations:

reuse of temporaries
constructor elision
improved alias analysis
elimination of variable access
loop unrolling

For an example:

const int nibbleCount = sizeof (int) * 2;
char formatted[nibbleCount + 1] = { 0 };

char* ToHex(int n)
{
for( int i = 0; i < nibbleCount; i++ )
{
   formatted[nibbleCount - i] = 0x30 + ((n >> (4 * i)) & 0x0f);
   if (formatted[nibbleCount - i] > '9') formatted[nibbleCount - i] +=
'a' - '0' - 10;
}
}

Take out the const, and the snippet won't even compile... and if it did, the
compiler couldn't unroll the loop.  With a constant value of nibbleCount,
however, the compiler can remove the loop entirely, eliminate the variable
i, and start using specialized instructions for accessing the different
bytes of the input.
Peter Duniho - 16 May 2007 19:48 GMT
> Well, for the specific case of a string literal, you can do:
>
[quoted text clipped - 3 lines]
>
> char[] asz = "This is a non-const literal string";

A few things.

First, I was under the impression that we were discussing the "const"  
keyword as it applies to function declarations.  The above are examples of  
"const"-declared constants, which C# does already have, and so aren't  
really what we were talking about.

Second, in the above example, the second line of code doesn't compile.  
You can fix the syntax error, by putting the [] in the right place (after  
the "asz" rather than after the "char"), but then the type of the variable  
is not the same as the first line of code.  You can't compare the results  
of the two, because you're changing more than just the "const"-ness.

Third, if make the two lines of code actually comparable, by fixing the  
type in the second line of code to be "char*", you'll find that it  
compiles to exactly the same instructions as the first line of code.  In  
other words, any difference between the two lines of code you posted (once  
you fix it so that it compiles) are caused *not* by the use of the "const"  
keyword, but rather by the differences in the way the compiler deals with  
a pointer versus an array.

> The second form requires the compiler to actually initialize the memory  
> on every entry to the function, instead of referencing a string  
> literal.  It
> also prevents string folding, which increases the size of the binary,  
> which has its own set of performance problems.

See above.  Any such problems are not due to the difference between  
something being "const" and not "const".  (Though, actually...I looked at  
the code generated and while it's true the array is initialized  
differently than the pointer, that initialization does not actually  
involve initializing the storage for the array itself...in other words,  
it's not like the string literal gets copied).

> Beyond that, pass-by-const reference is more performant than  
> pass-by-value for large data types.  Sure, you don't strictly need
> const for that, you can pass by reference and get most of the
> performance gains.

You don't even not strictly need const for that.  The use of the "const"  
keyword has nothing do with whether you can pass things by reference or  
not.  It makes it safer (which *is* the point of using "const"), but you  
can pass things by reference just fine without using "const".  In other  
words, in that example, "const" doesn't improve performance, it improves  
safety.

I certainly wouldn't try to disagree with the statement that "const"  
improves safety.  But that's not the question here.

> But, const also enables a lot of optimizations:
>
[quoted text clipped - 3 lines]
> elimination of variable access
> loop unrolling

As applied to function declarations, I don't see how "const" enables any  
of those optimizations.  The compiler can't rely on the "const" keyword,  
because it can always wind up being cast away.

> For an example:
>
[quoted text clipped - 17 lines]
> different
> bytes of the input.

Well, first of all, that code won't compile regardless.  You've got a  
function that is supposed to return a value, but it doesn't.

Secondly, the reason taking "const" out prevents it from compiling is that  
you can't initialize an array with a size that isn't known at compile  
time.  And of course, likewise the compiler can't make optimizations  
requiring constant values based on non-constant values.

For example, these two functions wind up compiled to be very different:

const int i = 5;
void Test1()
{
    printf("%d", i);
}

int j = 5;
void Test2()
{
    printf("%d", j);
}

I certainly don't debate that.  But the reason they are different is  
because in the first case, the value of the variable is known at compile  
time, while the value of the variable in the second case is not.  This has  
exactly nothing to do with the question of using "const" in a function  
declaration.

I thought maybe you had an example of how including "const" for function  
declarations in C# would help optimizations.  After all, C# already has  
"const" for variable declarations, and it has the same benefits in C# as  
it has in C++.  So there's not really any point in contrasting the two  
languages in that way.  So far, you haven't provided any actual examples  
of performance improvements that using "const" in a function declaration  
would enable.

Pete
Ben Voigt - 17 May 2007 15:32 GMT
Sorry about not compile-testing.

Here is an example that may work better for you:

/* in one compilation unit */
namespace { std::vector<int> v; }
void stuffit(int& n)
{
   v.push_back(n);
}

/* in another compilation unit, assuming that the proper prototype for
stuffit is available */
int main()
{
   for( int i = 0; i < 4; i++ ) {
       stuffit(i);
   }
   return 0;
}

Without "const", no optimizations are possible.  With "const int& n", the
compiler can fully unroll the loop and eliminate i.  Yes, I know that
passing an int by const reference in this case is silly, but it could easily
be a large data structure, and stuffit could be an arbitrary level of
complexity.

Or:

void demo(int i)
{
   int a[10];
   for( int j = 0; j < 10000; j++ )
   {
       a[13 * i % 7 + 3] += j;
       stuffit(i);
   }
}

With "const int& n", the compiler can determine that i is unchanging for the
duration of the loop, and by extension, the pointer a + 13 * i % 7 + 3 can
be pre-computed (and in fact the entire addition operation could be
pre-evaluated as 10000 * 10001 / 2 => 50005000).  Without "const", aliasing
analysis reveals that i can change, therefore the computation must be
executed inside the loop, with little opportunity for optimization.

And the const on a member function is equivalent to changing constness of
the 'this' parameter, so all the above applies.
Ben Voigt - 17 May 2007 15:36 GMT
>As applied to function declarations, I don't see how "const" enables any
>of those optimizations.  The compiler can't rely on the "const" keyword,
>because it can always wind up being cast away.

I absolutely disagree with this.  The onus is on the programmer not to cast
away const in any way that would make the compiler's optimizations incorrect
which rely on said const-ness.  Practically speaking, casting away const is
always an error, now that C++ has the mutable keyword.  But certainly the
compiler can and does rely on the const keyword, and you take your (process)
life in your hands when you cast it away.
Zytan - 16 May 2007 17:17 GMT
> Actually, in a good programmer's hands, that's a notice to the compiler that
> the function is not guaranteed to leave the data unchanged.  I'd hate for
[quoted text clipped - 7 lines]
> compile-time error checking, and enables automatic optimizations that
> couldn't otherwise be performed.

I got into this a lot back on the VB group, that there's a lot that
"const" does that people who have never used it would ever realize.
And when I say "used it", i mean actually used it 100% where it should
be used.  Without getting into detail, I'll mention just one benefit:
it's forced me to correct large architectural issues that I wasn't
aware of.  A lot power in a little word.

I still think C#'s designer's thoughts on how C++'s "const" only works
because it can be cast away is 100% completely false.  It's done
nothing but be a benefit for me.  (But, perhaps I just haven't seen
the situations he has seen.  But, perhaps his situations wouldn't have
happened had "const" been used from the get-go, though.  Perhaps an
architectural redesign wasn't worth having "const" in there, so they
cast it away, thinking "const is useless, you have to cast it away to
work", and that's the most likely scenario.)

Zytan
Zytan - 16 May 2007 17:12 GMT
> Personall, because of the false positive issue, I think such a warning  
> isn't a good idea.  You'd see it far too often when it wasn't legitimate  
> for it to actually be useful.

Yes, definitely for C++, since "const" was never really required.

But, for C#, the warning would only come up when you're potentially
modifying a temporary.  Usually you don't even want to be calling a
method AT ALL on one, so I don't think the warning would happen too
often when it shouldn't (but that's based on my limited experience).

> My feeling is that while it's possible theoretically to address the issue,  
> I think it's simpler to just make the language simple and consistent, and  
[quoted text clipped - 4 lines]
> attribute (explicit or implicit) creates far too many new hassles to make  
> it worth the trouble.

If it happens all the time, and the warning fires false positives,
then yes, it would just be a pain.  I didn't think that this would
really ever happen.

Just an idea. ;)

Zytan
Peter Duniho - 16 May 2007 19:51 GMT
> [...]
> But, for C#, the warning would only come up when you're potentially
> modifying a temporary.  Usually you don't even want to be calling a
> method AT ALL on one, so I don't think the warning would happen too
> often when it shouldn't (but that's based on my limited experience).

I disagree that you would never want to call a method on a temporary  
value.  In fact, because of the OOP nature of C#, you are almost always  
calling a method, if you are using a temporary value at all.

If anything, I could see adding a warning when you are modifying a public  
field of a value type that is a temporary instance.  That seems obviously  
unwise and reasonable for the compiler to complain.  But for methods, the  
method could be doing anything, and the compiler doesn't really know what  
that is.  In fact, for a well-designed value type (eg immutable), a method  
on a value type would *always* be safe on a temporary value.

Pete
Ben Voigt - 17 May 2007 15:37 GMT
>> [...]
>> But, for C#, the warning would only come up when you're potentially
[quoted text clipped - 12 lines]
> that is.  In fact, for a well-designed value type (eg immutable), a method
> on a value type would *always* be safe on a temporary value.

And an immutable value type would only have methods declared const, so
safety is properly noted by the compiler.

> Pete
Zytan - 15 May 2007 15:13 GMT
> 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?

No, I am not suggesting that.

I am suggesting that it could do it in the same way C++ does it.  C++
solved it, so it's solvable.  Likely, it's stored in the "metadata"
whatever that means, presumably in the information that it arrives at
when compiling the method, so that someone else who uses it doesn't
have to look at the implementation, just the analysis.

> And then that would show up in the method metadata, yes - but as it is,
> that information isn't there.

I know it lacks this information.  But, it could do it, there's no
doubt.

> Again, if "const" were available, that would make a lot of sense - but
> it isn't, for better or worse.

Yup.

> 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.

I think I like that C++ is a little closer to reality, that is, how
the computer does things.  C# is a little bit removed.  Each has its
advantages.  To some degree, a good program / programmer needs both.
So, you shouldn't have to "unlearn" anything, but you do only because
it hides the truth.  Of course, C++ does it's fair share of hiding
things, too, so an identical argument could be made on C++ vs
assembly.

Zytan
Peter Duniho - 15 May 2007 18:10 GMT
> [...]
> I am suggesting that it could do it in the same way C++ does it.  C++
> solved it, so it's solvable.

Well, note that C++ implements it by including the "const" right in the  
function name.  Also note that C++ doesn't use "const" in the way that  
you'd like C# to use it (see my previous post).  I'm not sure it's fair to  
say that "C++ solved it", since C++ doesn't really solve the problem we  
started out talking about.

Frankly, I think C# already has enough implicit code-generation gotchas as  
it is.  Things like overloading hiding certain type conversions, for  
example.  I doubt that even if C# had a "const" operator, I'd want it  
accessing elements in a List<> differently depending on whether I was  
calling a method immediately on the item, and whether that method was  
labeled "const".

Pete
Zytan - 15 May 2007 22:25 GMT
> Well, note that C++ implements it by including the "const" right in the  
> function name.  Also note that C++ doesn't use "const" in the way that  
> you'd like C# to use it (see my previous post).  I'm not sure it's fair to  
> say that "C++ solved it", since C++ doesn't really solve the problem we  
> started out talking about.

I think we are speaking about the same thing, so perhaps you
misunderstand my idea.  I just thought if C# had "const" like C++
does, then it could go one step further, and make use of it to show a
warning that you are calling a non-const (i.e. potentially modifying)
method on a temporary, which seems pointless.

> Frankly, I think C# already has enough implicit code-generation gotchas as  
> it is.  Things like overloading hiding certain type conversions, for  
> example.  I doubt that even if C# had a "const" operator, I'd want it  
> accessing elements in a List<> differently depending on whether I was  
> calling a method immediately on the item, and whether that method was  
> labeled "const".

No, I wouldn't want things to change depending if it had const-ness or
not!  It could just be used for a helpful warning, that's all: Why
call a method that changes the class when the class is a temporary,
and is thrown away before you can even see or use it?

Zytan
Peter Duniho - 15 May 2007 22:50 GMT
> I think we are speaking about the same thing, so perhaps you
> misunderstand my idea.  I just thought if C# had "const" like C++
> does, then it could go one step further, and make use of it to show a
> warning that you are calling a non-const (i.e. potentially modifying)
> method on a temporary, which seems pointless.

I think I cover this adequately in the article I just posted, but just for  
completeness...

The problem (well, "a problem" anyway) is that lots of methods are  
essentially "const" even though no one's bothered to mark them as such.  
For that matter, lots of methods are essentially "const" even though they  
can't be marked as such, because they might call some other code that is  
essentially "const" but which isn't marked as such.

I suppose you could work it the other way around, claiming that code is  
"const" unless otherwise marked (say, introduce an "unconst" keyword you  
have to use any time you want a method to be able to change things).  But  
then you'd have the language assuming a bunch of external functions are  
const even though they are not (or if the language doesn't do that, you  
wind up with the previous problem *plus* an inconsistency in how functions  
are treated).

> No, I wouldn't want things to change depending if it had const-ness or
> not!  It could just be used for a helpful warning, that's all: Why
> call a method that changes the class when the class is a temporary,
> and is thrown away before you can even see or use it?

Well, a *class* _isn't_ temporary.  If you get a reference from a List<>,  
then yes you get a copy of the reference, just like you get a copy of a  
value type.  But the reference refers to a single instance, and if you  
change that instance, that change is reflected whether you look at the  
copied reference you're using, or the original reference stored in the  
List<>.

Pete
Zytan - 16 May 2007 17:23 GMT
> I think I cover this adequately in the article I just posted, but just for  
> completeness...

Yes, sorry to continue this in 2 different sections.

> The problem (well, "a problem" anyway) is that lots of methods are  
> essentially "const" even though no one's bothered to mark them as such.

Yes, but you could mark them as "const" when the warning fires.
 
> For that matter, lots of methods are essentially "const" even though they  
> can't be marked as such, because they might call some other code that is  
> essentially "const" but which isn't marked as such.

Yes, and this forces you to mark these as "const".  And if, finally
you realize at the very end you can't change the last one, since it
ISN'T const, and it should be, and you fix a major design issue.

> > No, I wouldn't want things to change depending if it had const-ness or
> > not!  It could just be used for a helpful warning, that's all: Why
[quoted text clipped - 7 lines]
> copied reference you're using, or the original reference stored in the  
> List<>.

Sorry, you're right, classes are references, and the 'copy' is the
reference to it, so you still have access to the original.  And a
'class' is not a temporary, the reference to it is.

I *meant* to say 'struct' / value type, instead.  I'll try to be more
clear in the future.

Zytan
Peter Duniho - 16 May 2007 19:55 GMT
>> The problem (well, "a problem" anyway) is that lots of methods are
>> essentially "const" even though no one's bothered to mark them as such.
>
> Yes, but you could mark them as "const" when the warning fires.

Been there, done that.  In any sizable project, you can waste a whole day  
chasing down warnings and still not find all the places you need to add  
"const".

And even when you spend that time, frequently you reach a point where  
you're calling some third-party API that you don't have the freedom to  
change.  At that point, if the language doesn't allow you to cast away  
const-ness, you're stuck.  And allowing you to cast away const-ness  
significantly lessens the usefulness of "const", IMHO.

> Yes, and this forces you to mark these as "const".  And if, finally
> you realize at the very end you can't change the last one, since it
> ISN'T const, and it should be, and you fix a major design issue.

What do you do when you don't have control over the design that is causing  
you trouble?

>> > No, I wouldn't want things to change depending if it had const-ness or
>> > not!  It could just be used for a helpful warning, that's all: Why
[quoted text clipped - 5 lines]
> I *meant* to say 'struct' / value type, instead.  I'll try to be more
> clear in the future.

Okay...well, I can agree that calling a method that changes a struct when  
the struct is temporary isn't useful.  But how to implement this in a  
practical way is the question.

Pete
Ben Voigt - 17 May 2007 15:41 GMT
>>> The problem (well, "a problem" anyway) is that lots of methods are
>>> essentially "const" even though no one's bothered to mark them as such.
[quoted text clipped - 8 lines]
> you're calling some third-party API that you don't have the freedom to
> change.  At that point, if the language doesn't allow you to cast away

And that impeaches the design of said API and should seriously call into
question your decision to use it.  If a library designer can't get
const-correctness, how can they be expected to properly solve a bunch of
more devious concerns (parameter checking, buffer overflows, race
conditions, etc)?

> const-ness, you're stuck.  And allowing you to cast away const-ness
> significantly lessens the usefulness of "const", IMHO.
[quoted text clipped - 5 lines]
> What do you do when you don't have control over the design that is causing
> you trouble?

You let your boss know that the design has fundamental flaws, and be
thankful that const brought these to your attention before deploying said
flawed design in a critical scenario and maybe killing someone.

>>> > No, I wouldn't want things to change depending if it had const-ness or
>>> > not!  It could just be used for a helpful warning, that's all: Why
[quoted text clipped - 11 lines]
>
> Pete
Peter Duniho - 17 May 2007 17:35 GMT
>> And even when you spend that time, frequently you reach a point where
>> you're calling some third-party API that you don't have the freedom to
>> change.  At that point, if the language doesn't allow you to cast away
>
> And that impeaches the design of said API and should seriously call into
> question your decision to use it.

You mean, like the Windows API?

I suppose I could avoid writing Windows software, but...that severely  
restricts my market.

Pete
Ben Voigt - 17 May 2007 19:17 GMT
>>> And even when you spend that time, frequently you reach a point where
>>> you're calling some third-party API that you don't have the freedom to
[quoted text clipped - 4 lines]
>
> You mean, like the Windows API?

You're going to have to be far more specific -- there are a lot of different
libraries grouped under the heading of "the Windows API" and all the core
ones are const-correct, in my experience.

If you find a Windows API function that isn't const-correct, then yes, I
would avoid that library and re-implement the required functionality using
the core APIs (or find a solution that has already done so, in a trustworthy
manner).

> I suppose I could avoid writing Windows software, but...that severely
> restricts my market.
>
> Pete
Peter Duniho - 14 May 2007 19:46 GMT
> [...]
> I guess this could be shortened to:
[quoted text clipped - 4 lines]
> 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
Samuel R. Neff - 14 May 2007 20:25 GMT
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; }
}

>This returns the following error:
>"Cannot modify the return value of
[quoted text clipped - 27 lines]
>
>Zytan
Bruce Wood - 14 May 2007 21:31 GMT
> If you declare your structure to implement an interface and then
> declare the list as containing the interface instead of the structure,
[quoted text clipped - 90 lines]
>
> }

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?
Jon Skeet [C# MVP] - 14 May 2007 22:11 GMT
> 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.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Samuel R. Neff - 14 May 2007 22:27 GMT
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,
[quoted text clipped - 7 lines]
>>
>> 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?
Jon Skeet [C# MVP] - 14 May 2007 22:33 GMT
> 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 :)

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Christof Nordiek - 15 May 2007 12:02 GMT
> 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
[quoted text clipped - 4 lines]
> For code clarity and consistency I would lean towards using immutable
> structures, but it's an interesting technical clarification.  :-)

Yes, but unboxing had much less overhead. IIUC it's only one additional
indirection while fetching the value.
No object creation there.

Christof

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.