Just curious, is changing the size of a struct/value type a breaking change in C#? Structs tend to be more sensitive in terms of memory layout since altering them directly affects the size of arrays/other structs. Are there any examples of code that breaks, either binary-wise or source-wise, after the layout of a struct in a library it uses is changed?
NOTE: By "breaks," I mean it fails to compile at all or the IL is invalidated. So for example I wouldn't consider this a breaking change:
// My.Library v1
public struct MyStruct {}
// My.Library v2
public struct MyStruct { int _field; }
// App code
using My.Library;
using System.Runtime.InteropServices;
Console.WriteLine(Marshal.SizeOf<MyStruct>()); // before printed 1, now prints 4
because it still runs.
Changing size by adding fields is ok for strictly managed code.
Adding field(s) is non-breaking change as code will be re-JIT-ed with the new type and all allocations will use correct size. Since it is value type the new fields will be properly initialized with empty values anyway.
Removing/changing types of existing fields(s) or properties is definitely breaking change.
Value types are sealed - so no other libraries can derive from that type - so unlike classes they can't create problems with "this derived class did not implement a new virtual property/interface method".
Note: if a value type is used for interop or any other kind of binary serialization outside of your control than any change is breaking.
I.e. someone else used MyLib.Point {int x;int y;}
to save a list of points with binary serialization to a file. If now "MyLib" adds a new field to MyLib.Point
than the serialized data no longer can be read with binary serialization. Similar issue with native interop.
Yes, source code incompatibilities are definitely possible, even in strictly managed code, if you add a new field. Taking your example, this compiles with version 1 but not version 2:
MyStruct s;
Console.WriteLine(s);
The reason is that C# allows a struct local to be used if all the fields have been assigned values. In version 1, there are no fields so s
is "definitely assigned". However if a field is added in version 2, even if it is private, then this no longer compiles because s
is no longer definitely assigned.
This case should be binary compatible since the CLR guarantees initialization of fields to their default values.
Jared Parsons had a good blog post on the subject of private fields in structs where he details other cases where changing private implementation details would be dangerous (for unsafe code) or breaking.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With