could you please post a few notes about when structs are appropriate? That would help round this out.
In C#, you want to use a struct whenever you have something that should
_act like a value_. That's really the bottom line: is this something
that is involved in calculations (which don't necessarily have to be
traditional +, -, *, etc.). The world abounds with value types:
anything that is a measurement, for example, is a good candidate for a
value type in C#. Some examples: pressure, temperature, speed, weight,
dimensions, age. Monetary values also stand out as great examples.
"But wait," you say, "I can represent all of those things using the
decimal type!"
True, but then you lose track of units, unless you remember to encode
them in variable names. (In fact, that idea that "I could represent
that with a decimal," (or a double, or an int...) is a hint that maybe
it would make a good value type.) Contrast this:
decimal turtleSpeed = 1;
with this
Speed turtle1Speed = new Speed(1, SpeedUnits.Mph);
In the first case, you have no idea how fast that is. In the second
case, you know that it's pretty slow (well, not for a turtle, but it's
pretty slow for us). The payoff is that you can pass turtleSpeed
throughout your program as an argument, etc, and you always know that
it's 1 mile per hour, and not
Speed turtle2Speed = new Speed(1, SpeedUnits.Mach);
which would be a turtle shot from a cannon. Now, so far struct versus
class hasn't really bought us anything. It doesn't until you say:
Speed twoTurtleSpeed = turtle1Speed < turtle2Speed ? turtle1Speed :
turtle2Speed;
Here, a Speed acts like a value: twoTurtleSpeed is a copy of (in this
case), turtle1Speed, just as it would be if it were an int or a
decimal. The type also offers the possibility of automatic conversions:
here we compared miles per hour with Mach speed measurements and
decided which one was less. Try that with:
decimal turtle1MphSpeed = 1;
decimal turtle2MachSpeed = 1;
....your client code has to do the math, rather than relegating it to a
Speed struct, where it belongs.
I work in the wood industry (thus the moniker), and we do a lot of
mathematics with dimensions and weights. structs are a natural fit for
this. I can do things like this:
Measure thickness = new Measure(1.5, UnitOfMeasure.Inches);
Measure width = new Measure(15, UnitOfMeasure.Inches);
Measure length = new Measure(20, UnitOfMeasure.Feet);
Measure volume = thickness * width * length;
without worrying about conversion headaches, or what units "volume"
should have. The Measure class can take care of all of that for me.
Then I can pass "volume" to another method and that method will know
exactly what volume that represents, units and all:
decimal volumeInCcs = volume.GetAmount(UnitOfMeasure.Cc);
or
Measure volumeInCcs = volume.ConvertTo(UnitOfMeasure.Cc);
Automagical conversions, at your fingertips. Note that in the second
example conversion, the ConvertTo method _returns a new Measure_. It
does _not_ modify the value in "volume".
There are other uses for structs, of course: Microsoft made Point and
Rectangle structs, because (I believe) they do lots of coordinate math
inside their graphics packages. The only thing that MS did that I
disagree with is that they made them _mutable_ structs: structs with
"set" methods on their properties. I find that mutability causes more
problems than it solves in structs, and I prefer to avoid it. That
aside, any kind of coordinate system is a good candidate for structs,
as we naturally tend to think of coordinates as values. Imagine being
able to do this:
Coordinate point1 = new Coordinate(1.5, 6.6, 18.8);
Coordinate point2 = new Coordinate(95, UnitOfMeasure.Degrees, 52.2);
decimal distance = point1.DistanceTo(point2);
Mixing Cartesian and circular coordinate systems? No problem. It just
works.
Anyway, those are some examples. Note that the common denominator is
that all of these things naturally act like _values_: you expect them
to be copied, and you expect operations on them to yield new values,
not alter existing values. In fact, none of these things (a speed, a
temperature, a point in space, etc) has the concept of _identity_: one
point (5,5) is exactly like any other point (5,5). We typically don't
talk about "that 5,5" versus "this 5,5". It doesn't have a UID
(although it could _serve_ as a UID for something else).
Hope all of that helps.
I do still like my point about using delegation
Yes, I thought that was good, too.