更改结构的大小是否是 C# 中的重大更改?
Is changing the size of a struct a breaking change in C#?
只是好奇,更改 struct/value 类型的大小是否是 C# 中的重大更改?结构在内存布局方面往往更敏感,因为更改它们会直接影响 arrays/other 结构的大小。在它使用的库中的结构布局更改后,是否有任何代码中断的示例,无论是二进制方式还是源代码方式?
注意:"breaks," 我的意思是它根本无法编译或 IL 无效。因此,例如,我不会认为这是一个重大变化:
// 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
因为它还在运行。
对于严格托管的代码,可以通过添加字段来更改大小。
添加字段是非破坏性更改,因为代码将使用新类型重新进行 JIT,并且所有分配都将使用正确的大小。由于它是值类型,因此无论如何都会使用空值正确初始化新字段。
Removing/changing 类型的现有字段或属性绝对是重大更改。
值类型是密封的 - 因此没有其他库可以从该类型派生 - 因此与 类 不同,它们不会对 "this derived class did not implement a new virtual property/interface method" 产生问题。
注意:如果值类型用于互操作或超出您控制范围的任何其他类型的二进制序列化,则任何更改都会中断。
即其他人使用 MyLib.Point {int x;int y;}
将二进制序列化的点列表保存到文件中。如果现在 "MyLib" 向 MyLib.Point
添加一个新字段,那么序列化数据将无法再通过二进制序列化读取。本机互操作的类似问题。
是的,如果添加新字段,源代码不兼容肯定是可能的,即使在严格管理的代码中也是如此。以您的示例为例,它使用版本 1 而不是版本 2 进行编译:
MyStruct s;
Console.WriteLine(s);
原因是如果所有字段都已赋值,C# 允许使用局部结构。在版本 1 中,没有字段,因此 s
是 "definitely assigned"。但是,如果在版本 2 中添加了一个字段,即使它是私有的,那么它也不再编译,因为 s
不再是明确分配的。
这种情况应该是二进制兼容的,因为 CLR 保证将字段初始化为其默认值。
Jared Parsons good blog post 关于结构中私有字段的主题,他详细介绍了更改私有实现细节会很危险(对于不安全代码)或破坏的其他情况。
只是好奇,更改 struct/value 类型的大小是否是 C# 中的重大更改?结构在内存布局方面往往更敏感,因为更改它们会直接影响 arrays/other 结构的大小。在它使用的库中的结构布局更改后,是否有任何代码中断的示例,无论是二进制方式还是源代码方式?
注意:"breaks," 我的意思是它根本无法编译或 IL 无效。因此,例如,我不会认为这是一个重大变化:
// 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
因为它还在运行。
对于严格托管的代码,可以通过添加字段来更改大小。
添加字段是非破坏性更改,因为代码将使用新类型重新进行 JIT,并且所有分配都将使用正确的大小。由于它是值类型,因此无论如何都会使用空值正确初始化新字段。
Removing/changing 类型的现有字段或属性绝对是重大更改。
值类型是密封的 - 因此没有其他库可以从该类型派生 - 因此与 类 不同,它们不会对 "this derived class did not implement a new virtual property/interface method" 产生问题。
注意:如果值类型用于互操作或超出您控制范围的任何其他类型的二进制序列化,则任何更改都会中断。
即其他人使用 MyLib.Point {int x;int y;}
将二进制序列化的点列表保存到文件中。如果现在 "MyLib" 向 MyLib.Point
添加一个新字段,那么序列化数据将无法再通过二进制序列化读取。本机互操作的类似问题。
是的,如果添加新字段,源代码不兼容肯定是可能的,即使在严格管理的代码中也是如此。以您的示例为例,它使用版本 1 而不是版本 2 进行编译:
MyStruct s;
Console.WriteLine(s);
原因是如果所有字段都已赋值,C# 允许使用局部结构。在版本 1 中,没有字段,因此 s
是 "definitely assigned"。但是,如果在版本 2 中添加了一个字段,即使它是私有的,那么它也不再编译,因为 s
不再是明确分配的。
这种情况应该是二进制兼容的,因为 CLR 保证将字段初始化为其默认值。
Jared Parsons good blog post 关于结构中私有字段的主题,他详细介绍了更改私有实现细节会很危险(对于不安全代码)或破坏的其他情况。