operator new with "value type"

  • Thread starter Thread starter carnold
  • Start date Start date
C

carnold

Hello,

I'm a developer coming from C++ and Java ... I've going thru
"Effective C#" (which highly recommend for people coming from other
languages wanting to learn C#), and it states that "value types" are
always created on the stack, but then showed the "creation" of one w/
operator new. I guess it makes sense, but is it true that:

MyValueType t;

and

MyValueType t = new MyValueType();

are equivalent w/ regard to where the object is stored? That just
seems odd to me. I suppose this is probably in a FAQ somewhere (I
welcome any pointers to any FAQ list that answers this), but I
couldn't find it stated obviously (tho I didn't spend too long
looking :) ...

-Charlie
 
[...] I guess it makes sense, but is it true that:

MyValueType t;

and

MyValueType t = new MyValueType();

are equivalent w/ regard to where the object is stored? That just
seems odd to me.

To me too. But yes, the two are equivalent with respect to where the data
is stored. It just happens that the value types use the "new" operator
for initializing even though no actual memory is allocated in that case.

Pete
 
carnold said:
I'm a developer coming from C++ and Java ... I've going thru
"Effective C#" (which highly recommend for people coming from other
languages wanting to learn C#), and it states that "value types" are
always created on the stack

If it says exactly that, then it's wrong.
See http://pobox.com/~skeet/csharp/memory.html
but then showed the "creation" of one w/
operator new. I guess it makes sense, but is it true that:

MyValueType t;

and

MyValueType t = new MyValueType();

are equivalent w/ regard to where the object is stored?

Yes. The value of the variable is an instance of MyValueType, and it's
stored wherever the context of the variable is.
That just seems odd to me.

I think it makes perfect logical sense, but it *is* different to C++.
 
If it says exactly that, then it's wrong.
Seehttp://pobox.com/~skeet/csharp/memory.html

It doesn't say *exactly* that; I summarized. Dangerous thing to do
when asking a programming question, I know ... talking to (some)
computer programmers is second only to talking to computers with
regard to the level of detail necessary to keep from causing a core
dump (or the equivalent ;)

If the variable is created as a member of another, it's created in the
context of the other ... obviously? Maybe not. I guess it's worth
mentioning ...
Yes. The value of the variable is an instance of MyValueType, and it's
stored wherever the context of the variable is.


I think it makes perfect logical sense, but it *is* different to C++.

It's a well understood design principal that you shouldn't change the
meaning of familiar constructs. "new" has specific implications in
both C++ and Java that don't hold in C# ... I guarantee you I'm not
the first one to think changing those implications a bad idea. But I
guess when you balance it against the introduction of a separate
construct to differentiate "create an auto variable" vs. "create and
zero an auto variable" (a differentiation which seems to me to be of
very little use, but I'm probably overlooking an important use-case or
two), it's probably the lesser evil.

If you base the design of your language on another (as C# is based on C
++ and Java), differing from those languages is Bad (as you're going
against your "we should be the same" design principal). It may be the
lesser of two evils (different semantics vs. introducing Yet Another
C#-exclusive construct), but taking an attitude of "just because it's
different doesn't mean it's bad" is Only valid when "we should be the
same" isn't an Obvious and Key goal in the design.
 
[...]
It's a well understood design principal that you shouldn't change the
meaning of familiar constructs.

It's also well-understood that it's okay to have behaviors that are
different represented by the same syntax, as long as the behavior is
similar enough. That's why operator overloading, and even overloading of
methods, is not only but valuable in many cases.

It's not an either/or, black-and-white question.
"new" has specific implications in
both C++ and Java that don't hold in C# ... I guarantee you I'm not
the first one to think changing those implications a bad idea.

You don't need to guarantee him. He knows. He's heard me complain about
the very same thing in the past. :)

I still wish that a different syntax had been used, but I do understand
the basis for those who say that using "new" is better than other
alternatives. I don't necessarily agree, but I'm not the one who got to
design the language and the issue is too subjective for me to claim
there's an absolute truth to be found.

Pete
 
Just as a minor comment on the "new" thing... the use of the same
keyword certainly help keep things consistent when considering
generics... if there were two operators, then it would be a little
less clear what "new T()" meant for "SomeMethod<T>() where T : new()".
(I'm mainly thinking of default values, and "not found" return values
in the "bool TrySomething(out T)" pattern).

Yes, value-types have different behavior - but that isn't really
limited just to construction. They behave differently as method
arguments, for instance - yet the syntax is identical there... I'm not
sure it makes much benefit to single out construction for special
syntax. Personally I'm happy with the shared syntax, but I appreciate
that there is a little bit of a learning curve.

Marc
 
carnold said:
It doesn't say *exactly* that; I summarized. Dangerous thing to do
when asking a programming question, I know ... talking to (some)
computer programmers is second only to talking to computers with
regard to the level of detail necessary to keep from causing a core
dump (or the equivalent ;)

If the variable is created as a member of another, it's created in the
context of the other ... obviously? Maybe not. I guess it's worth
mentioning ...

Unfortunately it's not obvious to some. When people use the shorthand
of "structs are created on the stack, classes are created on the heap"
some people get confused - understandably, I guess.
It's a well understood design principal that you shouldn't change the
meaning of familiar constructs. "new" has specific implications in
both C++ and Java that don't hold in C# ... I guarantee you I'm not
the first one to think changing those implications a bad idea. But I
guess when you balance it against the introduction of a separate
construct to differentiate "create an auto variable" vs. "create and
zero an auto variable" (a differentiation which seems to me to be of
very little use, but I'm probably overlooking an important use-case or
two), it's probably the lesser evil.

Well, it's not a case of doing something with an "auto variable" (not
that such a term exists in C#) - it's a case of calling a constructor.
How do you call a constructor? With "new TypeName(...)". That's true
whether you're using the result as a parameter, as a local variable, as
a member variable, or anything else.

While I take your point about not changing familiar constructs, there
are plenty of far more subtle changes. Overloading doesn't work exactly
the same way in C# as in Java or C++, for example - but I suspect
you'll find that's far less well-understood than the simple
"new ...(...) calls a constructor" change. It's a reasonably simple
change which makes things very consistent.

Another change from C++ and Java: you can't fall through from one
switch case to another (although you can have multiple labels for the
same case). Is that a bad idea too? It removes a common source of
errors.

Heck, the whole type system in C# is different from unmanaged C++, so
there's already a lot of adjustment required. In the grand scheme of
things, I think this is a very minor thing to get your head round,
compared with the bigger mindset changes. I'm very keen on trying to
use the idioms of the language I'm using, rather than writing code with
an accent from another language - if you try to write C# as if it's C++
or Java, you *will* end up with bugs and/or inelegant code.

I'm sure the decision wasn't taken lightly - and I'd rather have a
language which takes an extra few minutes to understand while learning,
but which is then consistent, than one which decides to live with an
inconsistent or inappropriate approach just because a previous language
took a particular path.
If you base the design of your language on another (as C# is based on C
++ and Java), differing from those languages is Bad (as you're going
against your "we should be the same" design principal). It may be the
lesser of two evils (different semantics vs. introducing Yet Another
C#-exclusive construct), but taking an attitude of "just because it's
different doesn't mean it's bad" is Only valid when "we should be the
same" isn't an Obvious and Key goal in the design.

The *overall* look and feel of C# is like Java, but I certainly don't
begrudge the designers for making changes. There's a difference betweem
"familiar style" and "exact copy". Should they have used "import"
instead of "using"? "extends/implements" instead of ":"? I really have
no problem switching between the two, and the time taken to learn
differences like this is very small indeed.
 
Just as a minor comment on the "new" thing... the use of the same
keyword certainly help keep things consistent when considering
generics... if there were two operators, then it would be a little
less clear what "new T()" meant for "SomeMethod<T>() where T : new()".
(I'm mainly thinking of default values, and "not found" return values
in the "bool TrySomething(out T)" pattern).

Yup, that's true. Anyone arguing against using "new" to initialize a
struct should be forced to come up with a suitable alternative syntax so
that a generic class or method that has to initialize/instantiate some
instance of one or more type parameters for the generic can still be done
without caring whether the type is a value or reference type.

So, do I have an alternative syntax? No, not at the moment. I'll get
back to you. :)
Yes, value-types have different behavior - but that isn't really
limited just to construction. They behave differently as method
arguments, for instance - yet the syntax is identical there... I'm not
sure it makes much benefit to single out construction for special
syntax. Personally I'm happy with the shared syntax, but I appreciate
that there is a little bit of a learning curve.

It doesn't bother me that much now. In fact, it never really created a
significant problem for me, per se. The main problem was that it for a
little while led me to believe a few types were classes instead of
structs. I also have a vague memory of some sort of uninitialized
variable error that was somehow related to this "new" business, but at the
moment I can't actually remember why it was related (whatever it was, it
was more complicated than just "oh, you need new to initialize your value
type" but I don't recall the details).

But it didn't cause me any hard-to-figure out coding problems and of
course I eventually got better at thinking value-vs-reference type without
getting confused by the overlapping syntax.

I can't say I would ever lobby hard to change the syntax. But I do have
sympathy for those who feel compelled to complain about it. :) And
believe me, if I had a good idea for how to deal with that situation
better, I'd talk about it. I do see the benefit in the current syntax,
and I have no suggestions for something better. But that doesn't mean I
have to like it. :)

Pete
 
carnold said:
It's a well understood design principal [sic] that you shouldn't change the
meaning of familiar constructs. "new" has specific implications in
both C++ and Java that don't hold in C#

If that were true no language would look different from FORTRAN.

The trouble with saying "it's ... well understood" is that the passive voice
construction begs the question of *who* understands it. I sure didn't. And I
used to program in FORTRAN for a living. Face it, different languages have
different meanings for things that superficially are similar. (References
since C++, generics between Java and C#, to name two.)

In fact, within FORTRAN itself there were changes within "familiar
constructs". A loop index variable was scoped to the loop in some dialects,
but wider in others. The newer languages changed the meaning of the familiar
construct of "scope", and made it explicit, thus fixing a lot of weird bugs.

Jon Skeet said:
Another change from C++ and Java: you can't fall through from one
switch case to another (although you can have multiple labels for the
same case). Is that a bad idea too? It removes a common source of
errors.

Another case where it's a well-understood design principle that one should
change the meaning of familiar constructs, sometimes.
 
The main problem was that it for a
little while led me to believe a few types were classes instead of
structs.

I agree that this can be a pain, and it is *so* important to know if
you are talking about a class or a struct - I'm lazy, so I simply
change the IDE color of the various "User Types (...)" items; my
"Value Types" are in magenta, for example. Very helpful when you are
working with something unfamiliar.

(it obviously doesn't help with keyword aliases (int vs string), but
I'm pretty sure I know all of those ;-p)

Marc
 
Another change from C++ and Java: you can't fall through from one
switch case to another (although you can have multiple labels for the
same case). Is that a bad idea too? It removes a common source of
errors.

Ahh, but you *can* fall-through when you need to, using "goto case". The
best of both worlds -- protection against stupid errors, and the ability to
do what you need where you need it. Even a rather intuitive syntax for
letting the compiler know that the out-of-the-ordinary was intended.
 
Ahh, but you *can* fall-through when you need to, using "goto case". The
best of both worlds -- protection against stupid errors, and the ability to
do what you need where you need it. Even a rather intuitive syntax for
letting the compiler know that the out-of-the-ordinary was intended.

True - but I wouldn't personally call "explicitly saying where to go
next" as fall-through :)

(One slight pity about the C# 3 spec is that in one place where it
shows how C# has removed a common source of C++ errors, it uses an if
without braces - demonstrating how a completely different source of C+
+ errors is still alive and kicking in C#!)

Jon
 
Hi,


--
Ignacio Machin
http://www.laceupsolutions.com
Mobile & warehouse Solutions.
Jon Skeet said:
(One slight pity about the C# 3 spec is that in one place where it
shows how C# has removed a common source of C++ errors, it uses an if
without braces - demonstrating how a completely different source of C+
+ errors is still alive and kicking in C#!)

I have never seem that error, have you?
 
I have never seem that error, have you?

Yes, I have. Basically code which ended up reading:

if (someExpression)
FirstStatement();
SecondStatement();

but should have read:

if (someExpression)
{
FirstStatement();
SecondStatement();
}

Not my code, of course, but I a bug I fixed :)
 
Jon Skeet said:
Yes, I have. Basically code which ended up reading:

if (someExpression)
FirstStatement();
SecondStatement();

but should have read:

if (someExpression)
{
FirstStatement();
SecondStatement();
}

Not my code, of course, but I a bug I fixed :)

python programmers...
 
Jon said:
Yes, I have. Basically code which ended up reading:

if (someExpression)
FirstStatement();
SecondStatement();

but should have read:

if (someExpression)
{
FirstStatement();
SecondStatement();
}

Not my code, of course, but I a bug I fixed :)

It must have happen hundred of thousands of times.

The programmer writes:

if (someExpression)
SecondStatement();

He figure out that he does not need the { } because it is only
one statement.

Then later another statement has to be added and because it is a bit
hectic at the time then it end up as:

if (someExpression)
FirstStatement();
SecondStatement();

And suddenly some things stop working.

Arne
 
Lew said:
In fact, within FORTRAN itself there were changes within "familiar
constructs". A loop index variable was scoped to the loop in some
dialects, but wider in others. The newer languages changed the meaning
of the familiar construct of "scope", and made it explicit, thus fixing
a lot of weird bugs.

Fortran 90/95 ?

Not possible in 66 and 77.


(I know that C++ had such a problem before ANSI standardization)

Arne
 
Hi,


Interesting, it might be the case that I have not seen that much code that
was not mine :)

It would be overkill to force the use of { though.
 
Interesting, it might be the case that I have not seen that much code that
was not mine :)

It would be overkill to force the use of { though.

Would it really? I certainly find code much more readable that way, and
it *is* required for various constructs IIRC.

I use them everywhere, and haven't found any disadvantages to it.
 
Jon Skeet said:
Would it really? I certainly find code much more readable that way, and
it *is* required for various constructs IIRC.

do-while and switch require the braces
 
Back
Top