Struct vs Class

J

JohnGoogle

Hi,

Newbie question...

After a recent article in VSJ I had a go at implementing a Fraction
class to aid my understanding of operator overloading. After a previous
message someone suggested that I implement it as a struct rather than a
class which I did and all worked OK.

The simplest declaration for the struct is:

public struct Fraction
{
private Int32 _numerator;
private Int32 _denominator;

public Int32 numerator
{
get { return _numerator; }
}

public Int32 denominator
{
get { return _denominator; }
}

public Fraction(Int32 numerator)
{
this._numerator = numerator;
this._denominator = 1;
}

public Fraction(Int32 numerator, Int32 denominator)
{
this._numerator = numerator;
this._denominator = denominator;
if (denominator < 1)
throw new ArgumentException("Fraction denominator value
'" + denominator.ToString() + "' is invalid.");
}

// All other declarations follow (for overloads of
+,-,*,/,++,--,>,<, etc).
}


As you can see, it is essential that the denominator must be 1 or more.
As it was originally a class, the constructors ensured that the
denominator was >= 1. Alos, the numerator and denominator values are
read only as they can only be modified by operators such as +,-,++,*,/
etc (not shown).

However, during my testing I realised that, as a struct, it is possible
to declare a struct which is initialised to 0 so the denominator
becomes illegal.

I've found that you cannot declare a parameterless construction such
as:

public struct Fraction
{
private Int32 _numerator;
private Int32 _denominator;
....
public Fraction()
{
this._numerator = 0;
this._denominator = 1;
}
....
}

This gives a compiler error: 'Structs cannot contain explicit
parameterless constructors'.


Also, you cannot initialise the fields of a struct such as:

public struct Fraction
{
private Int32 _numerator;
private Int32 _denominator = 1;
....
}

This gives a compilation error: '_denominator': cannot have instance
field initializers in structs'.


Is there any way to force a field of a struct to be initialised to a
non zero value?

If not, I'll go back to implementing it as a class.

TIA,

John.
 
D

Dave Sexton

Hi,

You can't prevent the initialization of an instance of a struct with default
values. You'll always be able to code: Fraction fr = new Fraction(),
regardless of the constructors that you define.

If you decide to stick with a struct, don't forget to override Equals and
GetHashCode as well. And make sure that their return values are deterministic
and consistent with each other.
 
J

JohnGoogle

Thanks Dave,

I do override Equals and GetHashcode already. I guess I'll change it
back to a class. Thank you for you feedback. From what I had read, I
thought this was the case.

As a newbie, I'm interested in how much boxing / unboxing will be going
on when I define a struct as opposed to a class. I've just got Jeffrey
Richter's CLR via C# which seems to show me how to determine this via
the IL Disassembler. Some investigations to be done!

Thanks.

John.
 
J

Jon Skeet [C# MVP]

Is there any way to force a field of a struct to be initialised to a
non zero value?

No - and you'll always end up with the zero value if you create, say,
an array of the struct type.
If not, I'll go back to implementing it as a class.

I wouldn't make that the most important influence in your decision.
It's easy to ensure that the struct is initialised appropriately
wherever it's actually used. Think of a zero value for a struct as a
sort of equivalent (in this particular case) to a null value for a
reference type. Just as you could do:

FractionStruct x = new FractionStruct(); // x is useless at the moment

you could do:

FractionClass y = null; // y is just as useless at the moment


A fraction still feels more like a value type than a reference type to
me.
 
B

Bruce Wood

Thanks Dave,

I do override Equals and GetHashcode already. I guess I'll change it
back to a class. Thank you for you feedback. From what I had read, I
thought this was the case.

As a newbie, I'm interested in how much boxing / unboxing will be going
on when I define a struct as opposed to a class. I've just got Jeffrey
Richter's CLR via C# which seems to show me how to determine this via
the IL Disassembler. Some investigations to be done!

I, too, created a Fraction class for use in our company. Trust me:
don't make it a class. Simply assign meaning to "everything zero". It's
"essential" that the denominator be greater than zero only if you say
it's essential; you're writing the code, so you can make any
assumptions you want.

Simply write the internals so that denominator and numerator both zero
means "zero". Yes, it means more checks inside your code, but as I
said, trust me: making this a class will make it much less useful and
make it act much less intuitively. It should be a struct.
 
R

Ryan

You could make the _denominator field nullable and check for null in
the property:

public struct Fraction {
private Int32? _denominator;

[...]
public Int32 Denominator {
get{ if ( _denominator == null ) return 1; else return
(Int32)_denominator; }
}

[...]
}

Regards
,
Ryan
 
M

Martin Z

In general you want a way to flag the object as "uninitialized" - while
there are numerous clever ways to do it, ultimately the simplest
approach is to include a boolean. It's tempting to use a nullable
type, but afaik nullables are reference types, thus eliminating the
niceties using a struct. Structs work so much better when only
containing values.

As such, include a private boolean "isDenominatorIinitialized" - and
use

public Int32 Denominator {
get{ if (isDenominatorIinitialized == false ) return 1; else
return (Int32)_denominator; }
private set {isDenominatorInitialized = true; _denominator =
value; }
}

And then use the private set whenever you (internally) assign to the
denominator. This assumes 2.0 where you can have differing access to
get/set attributes. This should perform better than the nullable
approach.
You could make the _denominator field nullable and check for null in
the property:

public struct Fraction {
private Int32? _denominator;

[...]
public Int32 Denominator {
get{ if ( _denominator == null ) return 1; else return
(Int32)_denominator; }
}

[...]
}

Regards
,
Ryan

Hi,

Newbie question...

After a recent article in VSJ I had a go at implementing a Fraction
class to aid my understanding of operator overloading. After a previous
message someone suggested that I implement it as a struct rather than a
class which I did and all worked OK.

The simplest declaration for the struct is:

public struct Fraction
{
private Int32 _numerator;
private Int32 _denominator;

public Int32 numerator
{
get { return _numerator; }
}

public Int32 denominator
{
get { return _denominator; }
}

public Fraction(Int32 numerator)
{
this._numerator = numerator;
this._denominator = 1;
}

public Fraction(Int32 numerator, Int32 denominator)
{
this._numerator = numerator;
this._denominator = denominator;
if (denominator < 1)
throw new ArgumentException("Fraction denominator value
'" + denominator.ToString() + "' is invalid.");
}

// All other declarations follow (for overloads of
+,-,*,/,++,--,>,<, etc).
}

As you can see, it is essential that the denominator must be 1 or more.
As it was originally a class, the constructors ensured that the
denominator was >= 1. Alos, the numerator and denominator values are
read only as they can only be modified by operators such as +,-,++,*,/
etc (not shown).

However, during my testing I realised that, as a struct, it is possible
to declare a struct which is initialised to 0 so the denominator
becomes illegal.

I've found that you cannot declare a parameterless construction such
as:

public struct Fraction
{
private Int32 _numerator;
private Int32 _denominator;
....
public Fraction()
{
this._numerator = 0;
this._denominator = 1;
}
....
}

This gives a compiler error: 'Structs cannot contain explicit
parameterless constructors'.

Also, you cannot initialise the fields of a struct such as:

public struct Fraction
{
private Int32 _numerator;
private Int32 _denominator = 1;
....
}

This gives a compilation error: '_denominator': cannot have instance
field initializers in structs'.

Is there any way to force a field of a struct to be initialised to a
non zero value?

If not, I'll go back to implementing it as a class.

TIA,

John.
 
M

Martin Z

Bruce, My understanding is that, if designed to be entirely immutable
and containing no events, then there is almost no visible difference
between a class and a struct. The struct is primarily a
speed-optimization - access to stack-variables is much faster. Hence,
structs should only contain other valuetypes.
 
D

Dave Sexton

Hi Martin,
The struct is primarily a
speed-optimization - access to stack-variables is much faster. Hence,
structs should only contain other valuetypes.

But values can be stored on the heap as well.

Check out this article by Jon Skeet:
http://www.yoda.arachsys.com/csharp/memory.html

Structs that have private fields that reference objects simply store the
reference with the rest of the structure. Memory-wise, it's just like storing
Int32, AFAIK.

A value type is meant to be just that - a value. Values are not meant to be
tangible objects - like paper - they are meant to represent a value that you
can assign to an object - like worth (money). Therefore, it makes sense to me
that values are stored in-line with the objects that contain them, or on the
stack if that's where they are placed. They stay with their container because
they only have value, not presence.

I ask myself the following question when choosing between a struct or class
definition:

When an instance is used as a hash key, should the corresponding value in a
hash table be accessible by value or by reference only?

In other words, will I need the reference of the entity in order to retrieve
the keyed value at a later time, or just the value?

It's usually obvious whether to use a struct or a class from there, even if I
don't plan on using any instances to key a hash table. I never take into
consideration whether the struct will contain references, however I don't
think I've ever created a struct that had references anyway - so I might agree
with you after all, but for a different reason :)
 
B

Bruce Wood

Martin said:
Bruce, My understanding is that, if designed to be entirely immutable
and containing no events, then there is almost no visible difference
between a class and a struct.

Apparently so. I'm trying to think of differences in behaviour between
an immutable class and a struct, and I can't. Of course, it's Monday
morning. I could be wrong.

One would have to overload == appropriately (and, one presumes, the
other mathematical and comparison operators), but apart from that I
can't think of any hitches. Of course, the temptation with a class
would be to make it mutable, but that's just a temptation, not a given.
The struct is primarily a speed-optimization - access to stack-variables is much faster.

I wouldn't state that as the primary speed benefits of structs. I
consider the primary speed benefit of structs being that they don't
need to be garbage-collected. So, if you are doing extensive
calculations that involve the creation of myriad intermediate results
then structs are a better choice because objects created on the heap
have to be GC'd.

However, this is only one consideration in whether to make something a
struct or a class, and not even the most important. The most important
consideration, IMHO, is whether the thing you want to create displays
_value semantics_. There are lots of discussions in this newsgroup
about struct vs class and value vs reference semantics.
Hence, structs should only contain other valuetypes.

No, I disagree. For example, I have a struct called a Measure, which
contains a decimal quantity and a unit of measure. UnitOfMeasure is a(n
immutable) reference type. I think that our disagreement on this point
goes back to the speed benefits of structs: I see storage on the stack
as a side-effect of using structs, not as a motivation for using
structs, and so I don't see anything wrong with structs that contain
references. Of course, structs that contain references to _mutable_
objects can lead to some awfully puzzling, nearly-unpredictable
behaviour, so I wouldn't recommend that. :)
 
D

Dave Sexton

Hi Bruce,
Of course, structs that contain references to _mutable_
objects can lead to some awfully puzzling, nearly-unpredictable
behaviour, so I wouldn't recommend that. :)

DictionaryEntry is one that doesn't conform to that idea, however.
 
M

Martin Z

Holy poop, DictionaryEntry is mutable? (off to MSDN).... Whoa. Oh,
and my comment about avoiding including value-types in structs was from
the optimization direction - obviously if you're using structs because
you want value-type semantics then it doesn't matter what you stuff in
them (as long as they're immutable to avoid confusing aliasing
behaviour).

Funny that nullables are value-type - I recently got an error from the
XmlSerializer where it complained that they were reference types, and
thus couldn't be XmlAttributes. That was just silly.

Anyhow, I think the reason DictionaryEntry is mutable is just because
people got really sick of saying new DictionaryEntry<MyKeyType,
MyValueType> - not for any good technical reason. I think it's
hilarious that the structs have the nice immediate-setting semantics
for mutable structs
myStructClass foo;
foo.bar = 1;
foo.baz = 2;
//all parameters initialized, it's ready to use, no constructor
needed.

but you can only use them with mutable structs anyways, making it
completely useless since MS says to never-ever-do-mutable-structs. I
want the readonly attribute to apply to property setters, and then
integrate that with the 3.0 Object Initializers so we can have some
non-excruciating syntax for immutables.
 
D

Dave Sexton

Hi Martin,
obviously if you're using structs because
you want value-type semantics then it doesn't matter what you stuff in
them (as long as they're immutable to avoid confusing aliasing
behaviour).

What do you mean by "aliasing behaviour"?
Funny that nullables are value-type - I recently got an error from the
XmlSerializer where it complained that they were reference types, and
thus couldn't be XmlAttributes. That was just silly.

I think Nullable<T> should be a value type just like DictionaryEntry. They
each assign a value to the objects that they contain - nullability or hash
entry, respectively.

Although nullability is only useful for value types, which is why I suspect
that you can't make a Nullable<string>, for example.

But hash entries can be anything, including mutable objects. DictionaryEntry
is just a value: the relationship between a key and its corresponding value.
If the key or the value are object references then the value of the hash entry
itself is simply the relationship between the two references. If another
DictionaryEntry contains the same key and value references, then the values of
the DictionaryEntries are equal.

Nullable<T> and DictionaryEntry do not represent tangible objects, they
represent assignable values. It helps some people to think of values as
conceptual and objects as physical, like me!
Anyhow, I think the reason DictionaryEntry is mutable is just because
people got really sick of saying new DictionaryEntry<MyKeyType,
MyValueType> - not for any good technical reason.

I've never actually created an instance of DictionaryEntry though. I've only
used it in iterators so I'm not sure the mutability was necessary. I assume
the author(s) made it mutable due to some internal implementation constraints
that I don't feel like searching for right now :)

But just to clear things up, my response to Bruce wasn't about the mutability
of DictionaryEntry, it was about the mutability of the objects that it
references. That's what I assumed Bruce meant when he wrote, "structs that
contain references to _mutable_ objects", although reading it out-of-context
now I can see why you thought that.
I think it's
hilarious that the structs have the nice immediate-setting semantics
for mutable structs
myStructClass foo;
foo.bar = 1;
foo.baz = 2;
//all parameters initialized, it's ready to use, no constructor
needed.
but you can only use them with mutable structs anyways, making it
completely useless since MS says to never-ever-do-mutable-structs. I
want the readonly attribute to apply to property setters, and then
integrate that with the 3.0 Object Initializers so we can have some
non-excruciating syntax for immutables.

I won't argue that :)
 
M

Martin Z

structs containing references to mutable objects is what I meant by
"aliasing" - that is, if I have a struct I may assume that everything I
do with it will be copy-semantics... however, if it contains a
reference to a mutable class (say a stringbuilder) then if I make
changes to that stringbuilder, then all copies (not just the object I'm
accessing to make the changes) will see that, which is unexpected
behaviour for a struct. That is, an alias where we expect a copy.

And yes, I agree that Nullables _should_ be valuetype, and likely are
in implementation - I just thought that they were reference-types
because errors thrown by the XmlSerializer informed me that they were
reference types... which is likely just the serializer getting very
confused by the concept of a nullable value-type.
 
D

Dave Sexton

Hi Marin,
structs containing references to mutable objects is what I meant by
"aliasing" - that is, if I have a struct I may assume that everything I
do with it will be copy-semantics... however, if it contains a
reference to a mutable class (say a stringbuilder) then if I make
changes to that stringbuilder, then all copies (not just the object I'm
accessing to make the changes) will see that, which is unexpected
behaviour for a struct. That is, an alias where we expect a copy.

Thanks for clearing that up. I didn't realize that you were agreeing with
Bruce when you wrote, "aliasing".

But I think DictionaryEntry proves that it may be appropriate to define a
struct that has references to mutable objects. When DictionaryEntry contains
references, the value of the DictionaryEntry itself is the relationship
between the references. It doesn't matter whether the objects being
referenced can be mutated or not. Therefore, structs that assign value to
references or their relationships should have no problem referencing mutable
objects.
And yes, I agree that Nullables _should_ be valuetype, and likely are
in implementation - I just thought that they were reference-types
because errors thrown by the XmlSerializer informed me that they were
reference types... which is likely just the serializer getting very
confused by the concept of a nullable value-type.

Nullable<T> is an immutable value type.
That is a strange error - quite possibly a bug.
 
B

Bruce Wood

Martin said:
Anyhow, I think the reason DictionaryEntry is mutable is just because
people got really sick of saying new DictionaryEntry<MyKeyType,
MyValueType> - not for any good technical reason. I think it's
hilarious that the structs have the nice immediate-setting semantics
for mutable structs
myStructClass foo;
foo.bar = 1;
foo.baz = 2;
//all parameters initialized, it's ready to use, no constructor
needed.

but you can only use them with mutable structs anyways, making it
completely useless since MS says to never-ever-do-mutable-structs.

Do they? I've never seen them say this, but then I delve into the MSDN
doc only when I have to.

In fact, MS made some more famous mutable structs: Point and Rectangle.
I think that I understand why they did it: they did it so that people
could use syntax like this:

myPoint.X = 7;

rather than forcing them to create a whole new Point just to change the
X or Y coordinate, like this:

Point myPoint = new Point(7, myPoint.Y);

Yes, the former looks cleaner, but IMHO making Point mutable wasn't
worth all of the confusion it causes, and they would have been better
off to force us to use the second syntax.

I'm sure you know about the confusion: non-intuitive behaviour when a
property returns a Point, or when a Point is boxed for storage in an
aggregate structure. Many, many newbies try to use the above p.X = 7
syntax in those situations and then wonder why their point's
coordinates didn't change. Yuck.

I just plain don't indulge in creating mutable structs for questionable
improvements in syntax. If you want to change one aspect of a struct's
state, "new" up a new one. You just have to be sure to include a
constructor that takes all publicly-settable state as arguments.
 
D

Dave Sexton

Hi Bruce,

I'm sure you know about the confusion: non-intuitive behaviour when a
property returns a Point, or when a Point is boxed for storage in an
aggregate structure. Many, many newbies try to use the above p.X = 7
syntax in those situations and then wonder why their point's
coordinates didn't change. Yuck.
I just plain don't indulge in creating mutable structs for questionable
improvements in syntax
<snip>

I agree that structs should be immutable, without question. However, many,
many newbies do many, many things wrong. I'm not sure that I agree with the
reasoning behind your conclusion. Newbs seems to always catch all exceptions
too, but we really can't get rid of that functionality either.

I guess I'd prefer immutability in structs just so shrewd programmers don't
make the same mistake on accident. I can't think of a better reason than
that, unfortunately.
 
B

Bruce Wood

Dave said:
Hi Bruce,



<snip>

I agree that structs should be immutable, without question. However, many,
many newbies do many, many things wrong. I'm not sure that I agree with the
reasoning behind your conclusion. Newbs seems to always catch all exceptions
too, but we really can't get rid of that functionality either.

I guess I'd prefer immutability in structs just so shrewd programmers don't
make the same mistake on accident. I can't think of a better reason than
that, unfortunately.

LOL... well, I guess appealing to the newbie thing wasn't a good
argument. :)

What I meant to say is that if Location is a property of type Point,
then this looks as though it ought to work:

myRectangle.Location.X = 7;

but it doesn't. It's perfectly logical that it doesn't work, but the
logic is subtle and requires considerable knowledge of how .NET / C#
works. I dislike things that look as though they should do something
but which, for subtle reasons, do something else (or, in this case,
nothing). Granted, the compiler complains about this specific case, but
there are other cases in which it doesn't. I prefer that my code be
clearly readable to all: newbies and veterans alike, so I prefer to
give up syntactic sugar if it makes my code clearer.

It's so much easier to remember that you can't change a struct's
state--ever--than to remember that you can change it under some
circumstances but not under other circumstances, for perfectly logical
but non-obvious reasons. That just makes the code more difficult to
maintain, IMHO.

One of the clues that this is going on is when newbies (who usually
know other languages, so they're not new to programming, just new to
C#) make the same mistake over and over again. Now, sometimes the
feature is so truly useful that its very utility outweighs the
resulting confusion. Structs, for example, confuse the heck out of
people coming from the C/C++ world, but it's so very useful to be able
to create new objects with value semantics that I wouldn't give it up
to make the language easier to understand. Mere semantic sugar, such as

myPoint.X = 7;

is another thing entirely. I would rather live with more long-winded
code and do away with the confusion than have a handy shorthand that
then creates problems elsewhere in the language.

I've worked in several shops of mixed-language, mixed-skill
programmers, so I prefer code that someone not terribly familiar with
the language can understand. Mutable structs just throw a big wrench
into that, so I avoid them.
 
D

Dave Sexton

Hi Bruce,

I agree with your new reasoning :)

I guess I don't mind this too much:

Point pt = new Point(
[complex math for x goes here],
[complex math for y goes here]
);

I've used the format above a lot and it's just as clean as assignments that
follow construction, IMO. However, ".X = ..." and ".Y = ..." is a bit more
intuitive, although Point is probably a bad example of this because everyone
knows it's (x,y), but if it were a struct with several parameters then inline
construction might get confusing. Although, you can declare local variables
instead and pass the variables to the constructor. But that seems a bit
ridiculous just to construct a single value type.

Whatever - I'm just going to stick with the idea that all structs should be
immutable for now in light of the reasons that you have stated.
myRectangle.Location.X = 7;

I know this won't compile, but I think the only way for the compiler to miss
an assignment to a mutable struct that doesn't have a variable is when it's
boxed. I don't know what kind of effect this would have on the CLR, but maybe
a subsequent version of the framework could force immutability on all boxed
value-types and throw an exception on an assignment attempt. C# 8.5 perhaps?

Of course, I really don't know for sure if the compiler will miss non-variable
assignments on boxed structs only.
 

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