struct member initialization - member by member?!

  • Thread starter Thread starter emma middlebrook
  • Start date Start date
E

emma middlebrook

Hi

I discovered that if you declare a structure (and not 'new()' it) you
can then separately initialize its members and the compiler counts
those separate statements as a full initialization. That struck me as
a bit odd really as I would have thought it would have only bothered
about 'tracking' an 'atomic'/'complete' (via 'new()') initialization
not doing it 'bit by it'!!! Any comments? Seems a bit of an overhead
tracking memberwise initialization doesn't it? Any C# compiler people
out there?

Hoping for some comments to show me that this isn't such a weird thing
please.

Emma Middlebrook
(e-mail address removed)

struct Value
{
public int x;
public int y;
}

Value v2;
v2.x = 6; // v2 can not be used yet as 'unassigned' ...
v2.y = 7; // v2 can now be used as all its parts are initialized.
 
emma middlebrook said:
I discovered that if you declare a structure (and not 'new()' it) you
can then separately initialize its members and the compiler counts
those separate statements as a full initialization. That struck me as
a bit odd really as I would have thought it would have only bothered
about 'tracking' an 'atomic'/'complete' (via 'new()') initialization
not doing it 'bit by it'!!! Any comments? Seems a bit of an overhead
tracking memberwise initialization doesn't it? Any C# compiler people
out there?

Why would it be an overhead? Or rather, why do you care about the
overhead which only exists at *compile* time? I'd rather the compiler
kept track of a few extra things to prevent bugs, if it doesn't take
any extra time at runtime.
 
If you go through the documentation and specs.. you'll see that it says
'structs that are not initilized cannot be accessed or used'. This
doesn't imply that the runtime would do any sort of tracking, in fact,
the compiler will plainly give you a message that there's an error and
won't compile (till you've initialized struct data before using it).
So.. it only checks for validity upon struct data being accessed. Have a
look at the following sample program:

public struct Point
{
public int x, y;

public Point(int p1, int p2)
{
x = p1;
y = p2;
}
}

class MyClass {
static void Main(string args[]) {
Point p;
Console.WriteLine("X is: {0}", p.x);

// Compiler will perform a check hede and will abort..

p.x;
Console.WriteLine("X is: {0}", p.x);

/*
if you comment out the preceding statement, the program will work and
compile. Notice I didn't initialize y and it still worked. This shows
that the compiler doesn't check for 'every' field that's not initialized
but only when a field is being accessed */

}
}


-Andre
 
Exactly - another good point made :)

-Andre

Jon said:
Why would it be an overhead? Or rather, why do you care about the
overhead which only exists at *compile* time? I'd rather the compiler
kept track of a few extra things to prevent bugs, if it doesn't take
any extra time at runtime.
 
Another reason is that structs don't have inheritance and are allocated on
the stack (until they're boxed at which time they get copied onto the heap).
When they're on the stack, they can really be though of as just a grouping
of nested locals on the stack. Until you box them or pass them to some
other method the compiler treats them as such and allows each member to be
independently initialized and used accordingly. This allows methods to
dynamically initialize certain members based on calculated information,
rather than requiring heavy handed stuff like forcing you to zero initialize
everything and then overwrite that with the real value later (plus it
prevents you from accessing the member before the 'real value' has been
set). Now when you pass that struct to some other method or cause it to be
boxed, the method being called has the implicit contract that input
parameters are fully initialized, and so at that point the compiler requires
that all members have been initialized. A similar statement is true for
boxing.
 
Hi Grant,

"> Another reason is that structs don't have inheritance and are allocated
on
the stack (until they're boxed at which time they get copied onto the
heap).

Not quite accurate, though. ValuTypes can be created unboxed in the managed
heap as part of an reference type members.
This allows methods to
dynamically initialize certain members based on calculated information,
rather than requiring heavy handed stuff like forcing you to zero initialize
everything and then overwrite that with the real value later (plus it
prevents you from accessing the member before the 'real value' has been
set).

In the case of creating value type in managed heap all members of the value
type is guaranteed to be zeroed. which means: reference type is set to null,
scalar to 0 and boolean to false. I this particular case c# will not
complain if you use the value without prior initialization.

To be completely correct it will warn you that you gonna use the default
value of the member.
Now when you pass that struct to some other method or cause it to be

B\rgds
100
 
Jon Skeet said:
Why would it be an overhead? Or rather, why do you care about the
overhead which only exists at *compile* time? I'd rather the compiler
kept track of a few extra things to prevent bugs, if it doesn't take
any extra time at runtime.

Why do I care: to broaden my knowledge and better understand what is
happening. I didn't say I was unhappy about any compile-time cost -
just that it seemed surprising.

Thanks

Emma Middlebrook
(e-mail address removed)
 
Andre said:
If you go through the documentation and specs.. you'll see that it says
'structs that are not initilized cannot be accessed or used'. This
doesn't imply that the runtime would do any sort of tracking, in fact,
the compiler will plainly give you a message that there's an error and
won't compile (till you've initialized struct data before using it).
So.. it only checks for validity upon struct data being accessed. Have a
look at the following sample program:

Who mentioned the run-time tracking anything? Also, as you point out
in your little code snippet, the doc/specs aren't correct if they say
what you quote above - it's possible to use any member of a struct as
long as it's been initialized. As I said, I find it weird that it
allows parts of a partially initialized type to be used. Are you and
Jon both saying you find this quite natural and expected in a language
making quite an effort to stop the programmer shooting themselves in
the foot? It's not a big deal but I was surprised to find that
partially initialized types could be used.

Emma Middlebrook
(e-mail address removed)
 
Andre said:
If you go through the documentation and specs.. you'll see that it says
'structs that are not initilized cannot be accessed or used'. This
doesn't imply that the runtime would do any sort of tracking, in fact,
the compiler will plainly give you a message that there's an error and
won't compile (till you've initialized struct data before using it).
So.. it only checks for validity upon struct data being accessed. Have a
look at the following sample program:

Who mentioned the run-time tracking anything? Also, as you point out
in your little code snippet, the doc/specs aren't correct if they say
what you quote above - it's possible to use any member of a struct as
long as it's been initialized. As I said, I find it weird that it
allows parts of a partially initialized type to be used. Are you and
Jon both saying you find this quite natural and expected in a language
making quite an effort to stop the programmer shooting themselves in
the foot? It's not a big deal but I was surprised to find that
partially initialized types could be used.

Emma Middlebrook
(e-mail address removed)
 
emma middlebrook said:
Thanks for your reply - I think you have hit on the real nub - I would
like to think of a class/struct as a type that should be either
uninitialized or initialized completely. I don't like this 'weak' idea
of a struct being a loose grouping of members and that its parts can
be used if those parts happened to have been initialized. However, I'm
really glad you mentioned the point that once the struct is passed to
some other method then it has to completely initialized. I'm happy
enough now and have confirmed this to be true both for passing structs
by value and passing structs by reference. This is good news and, to
be honest, I don't care that you can fiddle with the parts and use the
parts 'locally'.

I think it's actually rather helpful that you can fiddle with the parts
locally - you may want to set up one variable based on some
calculations with another, for instance. It would be a pain if you were
forced to use a separate local variable for that.

On the other hand, I'm not entirely sure how this works when the struct
uses properties instead of public fields... I guess you've got to use
"new" in that case.
 
You are correct on all counts. Thanks for correcting my sloppiness.

One other case where structs can be constructed on the heap: as elements on
an array. In that case also, they are zero initialized.
 
Back
Top