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

C

Christof Nordiek

Jon Skeet said:
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?

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
 
C

Christof Nordiek

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.

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
 
Z

Zytan

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
 
P

Peter Duniho

[...]
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
 
P

Peter Duniho

[...]
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
 
Z

Zytan

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.

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
 
Z

Zytan

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
 
P

Peter Duniho

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
 
P

Peter Duniho

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
 
B

Ben Voigt

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

Peter Duniho

[...]
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.
 
B

Ben Voigt

Peter Duniho said:
[...]
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.

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

Peter Duniho

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
 
B

Ben Voigt

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

Zytan

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

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
 
Z

Zytan

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.

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
 
Z

Zytan

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

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
 
P

Peter Duniho

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";

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:

reuse of temporaries
constructor elision
improved alias analysis
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:

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.

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
 
P

Peter Duniho

[...]
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
 
P

Peter Duniho

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?
[...]

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
 

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