MS *_*nth 22 c# properties abi
时隔很长一段时间,我又回到了 C#,并试图使用《C# 10 in a Nutshell》一书来跟上进度。
\n作者在那里提到,将属性的访问器从 更改为init或set反之亦然是一个重大更改。我可以理解如何将其更改为set可能init是一个重大更改,但我只是无法理解为什么以其他方式更改它会是一个重大更改。
例如:
\n// Assembly 1\nTest obj = new(){A = 20};\n\n// Assembly 2\nclass Test\n{\n public int A {get; init;} = 10;\n}\nRun Code Online (Sandbox Code Playgroud)\n即使我将init属性访问器更改为.Assembly 1 中的此代码也不应受到影响set。那么为什么这是一个重大变化呢?
Swe*_*per 20
这是因为init访问器被编译成带有modreq声明的设置器。int 属性的 IL 代码P可能如下所示(请参阅SharpLab):
.method public hidebysig specialname
instance void modreq([System.Runtime]System.Runtime.CompilerServices.IsExternalInit) set_P (
int32 'value'
) cil managed
{
...
}
Run Code Online (Sandbox Code Playgroud)
注意令牌modreq([System.Runtime]System.Runtime.CompilerServices.IsExternalInit)。
普通的 setter 不会生成 this modreq。
在调用方方面,modreq当且仅当modreq该方法中存在 a 时,调用指令必须提供声明作为要调用的事物的签名的一部分。因此,对访问器的调用init将如下所示:
callvirt instance void modreq([System.Runtime]System.Runtime.CompilerServices.IsExternalInit) SomeClass::set_P(int32)
Run Code Online (Sandbox Code Playgroud)
不仅仅是
callvirt instance void SomeClass::set_P(int32)
Run Code Online (Sandbox Code Playgroud)
如果更改为 setter,则init必须更改对访问器的所有调用以删除modreq,否则将无法正确解析该方法。因此,这是一个重大变化。
至于为什么modreq使用 代替常规属性来标记属性,请参阅规范草案中的这一部分。总而言之,这是二进制兼容性和“不知道访问器的编译器会做什么”之间的权衡init。最后,他们决定牺牲二进制兼容性,以便不了解的编译器init 不允许设置该属性的代码。
Mat*_*son 11
这记录在设计说明中,特别是标题为“重大更改”的部分中。
在元数据期间发出时,init 属性访问器的发出策略必须在使用属性或 modreq 之间进行选择。这些有不同的权衡需要考虑。
使用 modreq 声明注释属性集访问器意味着 CLI 兼容编译器将忽略该访问器,除非它理解 modreq。这意味着只有知道 init 的编译器才会读取该成员。不知道 init 的编译器将忽略 set 访问器,因此不会意外地将属性视为读/写。
modreq 的缺点是 init 成为 set 访问器的二进制签名的一部分。添加或删除 init 将破坏应用程序的二进制兼容性。
最终的决定是:
鉴于也没有对删除 init 作为二进制兼容更改的重要支持,因此选择直接使用 modreq。
因此他们modreq在实现中使用了,结果用替换init将set是一个二进制破坏性更改。
如果您确实想了解更多信息,可以在CLI 规范modreq中查找一些详细信息。
| 归档时间: |
|
| 查看次数: |
1566 次 |
| 最近记录: |