Iterations variables are read-only, and one would normally be forbidden from modifying a read-only structure directly, or passing such a structure or any part thereof by ref
to any code which would modify it. Unfortunately, there s a wrinkle: even though many struct methods and properties don t mutate the underlying structure, calling a method or property on a structure requires that it be passed by ref
whether or not the method or property will alter the struct instance. This left the designers of C# with five choices:
- Don t allow struct methods or properties to be used on structures in read-only context without explicitly copying the structs to a mutable storage location (most likely a local variable).
- Allow struct methods or properties to be used on structures in read-only context, and hope that they won t modify anything they re not supposed to.
- When calling a method or a property on a read-only struct, silently make a copy of the struct and give that copy to the method or property.
- Provide a means by which struct methods or properties can indicate declaratively whether or not they will mutate the underlying structure; only allow struct methods and properties to modify their own fields if they are declared as doing so, and only allow read-only structs to be passed to methods which are not declared as modifying them.
- Provide a means by which struct methods or properties can indicate declaratively whether or not they will mutate the underlying structure; forbid the use of such methods and properties on structures in read-only contexts, but make a defensive copy of read-only structures when calling methods without such declarations just in case the methods should have them but don t.
Choice #1 would be a little annoying, especially with regard to structs that expose their state via properties. Choice #2 or #4 would be good from a performance standpoint, but struct routines which unexpectedly write this
could cause unexpected behavior. Microsoft opted for #3 (choice #5 would be the same except for the addition of optional attributes). In many ways, that s the worst choice (methods which don t write this
will run slower than they would without the copy operation, methods which do write this
won t work correctly with or without it, and the only thing damaged when a struct writes a read-only variable would be an instance of a struct whose code was already broken). Nonetheless, that choice is by now well established, and it is what it is.
Incidentally, even read operations structures nested within readonly structures or properties of structure type can be much more expensive than using non-read-only fields but simply refraining from writing them. When calling myStruct.innerStruct.AddOne()
, the compiler first makes a copy of MyStruct
, and then makes a copy of myStruct.InnerStruct
, and then calls the AddOne()
method on that. By contrast, when calling myStructCopy.innerStruct.AddOne()
, it can pass innerStruct
by reference to the AddOne()
method without copying anything.