Fields which are of structure types allow direct access to the members thereof, while properties of such types do not. Consequently, if Thing.Boz
were a field of type Point
, code which wants to modify its X
value could simply say Thing.Boz.X += 5;
; if Thing.Boz
were a mutable property, it would be necessary to use var tmp = Thing.Boz; tmp.X += 5; Thing.Boz = tmp;
. The ability to write things more cleanly with the exposed field is often, but not always, a blessing.
If it will always be possible for Boz
to be a field, modifying its members directly will be cleaner, faster, and better than copying it to a temporary variable, modifying that, and copying it back. If the type of Boz
exposes its mutable fields directly (as structures should) rather than wrapping them in trivial wrappers, it will also be possible to use things like Interlocked
methods on them--something that's simply impossible with properties. There's really only one disadvantage to using fields in that way: if it's ever necessary to replace the field with a property, code which relied upon the thing being a field will break, and may be hard to fix.
In short, I would posit that in cases where one isn't concerned about being able to swap in different versions of code without having to recompile any consumers, the biggest effect of using properties rather than fields is to prevent consumers of the code from writing code which would take advantage of (and rely upon) the semantics of exposed fields which are of structure types.
Incidentally, an alternative to exposing a field of a structure type would be to expose an ActOnXXX
method. For example:
delegate void ActionByRef<T1>(ref T1 p1);
delegate void ActionByRef<T1,T2>(ref T1 p1, ref T2 p2);
delegate void ActionByRef<T1,T2,T3>(ref T1 p1, ref T2 p2, ref T3 p3);
// Method within the type that defines property `Boz`
void ActOnBoz<T1>(ActionByRef<Point, T1> proc, ref T1 p1)
{
proc(ref _Boz, ref p1); // _Boz is the private backing field
}
Code which wanted to add some local variable q
to Thing.Boz.X
could call Thing.ActOnBoz((ref Point pt, ref int x) => {pt.X += x;}, ref q);
and have the action performed directly on Thing._Boz
, even though the field is not exposed.