Structs with exposed fields are not evil. While some really old compilers would accept code like:
List<Point> myList;
myList[4].X = 5;
which would copy myList[4] to a temporary struct, modify the field X of that temporary struct, and then throw the modified struct away, and making structs immutable was a way to ensure that the compiler would squawk at the above construct, a better way of ensuring that a compiler would squawk at such code would be to change the compiler to forbid writes to fields of temporary structures. Given that such code has forbidden by compilers for a long time, structs with exposed fields are often the best way of holding things that have a fixed number of independent data items (e.g. Point, Rectangle, etc.)
What are problematic are structs in which functions other than constructors or property setters modify this
. While compilers will recognize that a write to someStructProperty.someField
is attempting to modify someStructProperty
, and will forbid it in cases where such modification wouldn't actually work, there is unfortunately no means for the compiler to know that someStructProperty.MutatingFunction()
would attempt to modify a temporary instance of the struct. The compiler would thus permit such code even though it couldn't actually work as intended. What I would suggest as a remedy in cases where a function is supposed to modify a struct "in place" would be to define a static method which takes an instance of the struct as a ref
parameter. For example, SetPointXY(ref Point pt, int x, int y)
. The compiler will regard the passing of a struct as a ref
parameter as an attempt to modify that struct, and will only allow it in situations where it will actually work.
Note that from a performance standpoint, the only time that writing struct fields individually wouldn't be the fastest way to update a struct would be when one is making most of the struct match either the default value or some other pre-existing struct. There may be some such cases where it would be faster to overwrite a struct with a pre-existing instance and then write the fields that should hold something different, but in general I would suggest that one should use methods or functions to update structs only when code which does so would be more readable than code which sets fields individually. If AreaCode
is a public field of type PhoneNumber
, the effects of somePhoneNumber.AreaCode = "847";
are far clearer than the effects of somePhoneNumber = new PhoneNumber("847", somePhoneNumber.Exchange, somePhoneNumber.Number, somePhoneNumber.Extension);
. Among other things, one would have to study the entire structure to know whether the latter would change any fields other than AreaCode
. On the other hand, if one's goal was in fact to have a structure which was blank except for some all-new data, using a constructor or factory method might help make clear the fact that one was doing so; if one simply overwrites all the fields of the struct individually, one would have to study the whole struct to know that there weren't any fields that would be left unchanged.