Joseph said:
So what is the specific utility of structs? I assumed that they were chiefly
a handy way to group value types, like Point and Vector3 do. It seemed
wasteful to me to make a class for three ints. I just assumed that object
creation is slower, but I guess that everything in .NET is an object anyway.
When you declare a struct, you create a _value_ type. That means that
it acts like an int or a double. Whenever you assign it:
MyValudType b = new MyValueType(...);
MyValueType a = b;
it is copied, just like an int or a double. Whenever you put it in a
collection (such as an ArrayList or a Hashtable) it is boxed. In
particular, if you make your value type mutable (not recommended) then
you have to be very, very careful that you understand the exact
semantics so that when you change the value you know that it changes
and you know exactly where it changes and where it doesn't. (Using the
example above, if you were allowed to say a.MyProperty = 5 then b would
not change, because the contents of b were copied into a on
assignment.)
If in your case your three ints are spacial coordinates (x, y, z) and
they were immutable (that is, you couldn't say coord.X = 5), then a
struct would probably be the right way to go. If you make them mutable
(coord.X = 5 is legal) then you could use a struct, but you would have
to be very careful. (Search this newsgroup to see the kind of confusion
that the behaviour of System.Drawing.Point causes.)
The bottom line is that in .NET structs come with important semantic
considerations: value semantics versus reference semantics. As such,
they shouldn't be used in an offhand manner as a sort of "classes lite"
because they introduce major changes in behaviour.
In my particular situation, I'm primarily interested in being able to look
up a given object with a specific 3 int location, without having to create
throwaway object intermediaries for comparisons.
Well, keep in mind that the only thing you really save by using structs
is memory management costs of allocating space for an object and then
garbage collecting it later. That is, unless your structs are being
boxed (as they would be if you used them in a Hashtable either as keys
or values). In that case you don't save anything.
Microsoft made System.Drawing.Point a struct because (IMHO) they do a
lot of mathematics with points, and didn't want to create a gazillion
Point objects as a result of intermediate calculation results and then
have to garbage collect them all later. If it's just a question of the
Hashtable, then I would say that it's a non-issue: your decision as to
whether to use struct or class should rest with higher-level conceptual
issues, not with fine points of efficiency.
Perhaps for my purposes, since I know that none of the ints will be larger
than, say, X, I could create a unique number from my int[a,b,c] that is
a + (b*(X+1)) + (c*(X+1)*(X+1))
and use this in the overrides you mentioned.
Yes... that would probably be a good strategy, so long as X never
changes during a particular run of your program. Another way is to
leverage off of the existing GetHashCode method in int:
public override int GetHashCode()
{
return a.GetHashCode() + b.GetHashCode() + c.GetHashCode();
}
or even
public override int GetHashCode()
{
return a + b + c;
}
Remember... you're not after a unique value, just a value that will
provide a nice, even distribution across all ints.
I wish I knew exactly how .NET generated the default hash codes.
Well, the default for Object probably just returns zero or something,
or maybe a fixed value for each object type. Each of the Framework
classes that overrides GetHashCode has its own algorithm. I tend to
assume that those are good, and leverage off of them when I can. For
example, if my class needs to hash on two key string fields, I just say
return string1.GetHashCode() + string2.GetHashCode();
assuming that Microsoft pays math wizards big bucks to come up with
good hash functions for strings.
