If the purpose of a data type is to encapsulate a fixed set of related but independent values (such as the coordinates of a point), an exposed-field structure meets that description better than any other data type. Such structures should not have many instance methods other than overrides of ToString()
, Equals()
, and GetHashCode()
; it's generally better for structures to use static utility methods than instance methods. For example, this would be problematical:
public void UpdateRange(Point pt)
{
if (pt.X > xmax) xmax = pt.X;
if (pt.Y > ymax) ymax = pt.Y;
if (pt.X < xmin) xmin = pt.X;
if (pt.Y < ymin) ymin = pt.Y;
}
but this would not:
public void UpdateRange(ref bounds it, Point pt)
{
if (pt.X > it.xmax) it.xmax = pt.X;
if (pt.Y > it.ymax) it.ymax = pt.Y;
if (pt.X < it.xmin) it.xmin = pt.X;
if (pt.Y < it.ymin) it.ymin = pt.Y;
}
Note that when classes expose properties of a structure type, it is not possible to modify such properties directly. One cannot use something like:
bounds.UpdateRange(ref myList[4], newPoint);
nor, if UpdateRange were an instance method, could one use:
myList[4].UpdateRange(newPoint);
In the latter situation, the code would compile, but wouldn't work. Instead, one has to use something like:
var temp = myList[4];
bounds.UpdateRange(ref temp, newPoint);
mylist[4] = temp;
Note that the instance method and the static method with a ref
parameter are semantically identical in the cases where both will compile. The difference between them is that the static method with a ref
parameter will only compile in cases where the ref
parameter is a modifiable variable, but calling the instance method on a property will cause the compiler to copy that property to a temporary variable, call the instance method on that, and discard the result.
I would suggest that your type is almost a perfect example of something that should be a structure. If it were a mutable class type, it would be unclear when code which passed a reference to an instance was really passing the instantaneous values the instance happened to hold at the time of a call, or was passing a reference to a "live" entity. Using an immutable class type or a so-called immutable struct type [note that there isn't really any such thing as an immutable value type] would make methods like UpdateRange
slower to write and to run, while offering no particular benefit.
The one essential thing to note about the structure type is that each field (e.g. xmin
) has no meaning other than "the last value that something stored in "xmin", or zero if nothing has been stored there yet. If code writes 256 to xmin
and -234 to xmax
, then xmin
will hold 256 and xmax -234. Any code which takes a
bounds` and does anything with it should be prepared for such values just as it would be if it took those fields as four separate parameters.