在很长一段时间之后,我又回到了C#,并试图用C#10这本书来迎头赶上.

那里的作者提到,将属性的访问器从init更改为set或反之亦然是一个突破性的变化.我能理解为什么把它从set改为init会是一个突破性的变化,但我就是不明白为什么反过来改变它会是一个突破性的变化.

例如:

//Assembly 1
Test obj = new(){A = 20};

//Assembly 2
class Test
{
   public int A {get; init;} = 10;
}

即使我将init属性访问器更改为set,程序集1中的此代码也不会受到影响.那么,为什么这是一个突破性的变化呢?

推荐答案

这是因为init个访问器被编译成一个带有modreq声明的setter.Int属性P的IL代码可能如下所示(请参见第SharpLab部分):

.method public hidebysig specialname    
    instance void modreq([System.Runtime]System.Runtime.CompilerServices.IsExternalInit) set_P (    
        int32 'value'   
    ) cil managed   
{   
    ...
}

请注意 token modreq([System.Runtime]System.Runtime.CompilerServices.IsExternalInit).

普通的setter不会生成这个modreq.

在调用方,当且仅当该方法上存在modreq时,Call指令必须提供modreq声明作为要调用的事物的签名的一部分.因此,对init访问器的调用将如下所示:

callvirt instance void modreq([System.Runtime]System.Runtime.CompilerServices.IsExternalInit) SomeClass::set_P(int32)

而且不仅仅是

callvirt instance void SomeClass::set_P(int32)

如果更改为setter,则必须更改对init访问器的所有调用以删除modreq,否则它将无法正确解析该方法.因此,这是一个突破性的变化.

至于为什么使用modreq而不是常规属性来标记特性,请参见规范草案中的this section.总而言之,这是二进制兼容性和"编译器不知道init个访问器会做什么"之间的权衡.最后,他们决定牺牲二进制兼容性,以便不了解init doesn't的编译器允许代码设置该属性.

Csharp相关问答推荐

我如何才能获得被嘲笑班级的私有成员?

无法将blob发送到Azure -缺少HTTP标头异常

Dapper是否可以自动扩展类成员

将修剪声明放入LINQ中

(乌龙)1&#比c#中的UL&#慢吗?

Nuget包Serilog.Sinks.AwsCloudwatch引发TypeLoadExceptions,因为父类型是密封的

返回TyedResults.BadRequest<;字符串>;时问题详细信息不起作用

为什么SignalR在每个Blazor服务器应用程序启动时最多启动8个服务器?

发布用于Linux Ubuntu的C#应用程序

正确处理嵌套的本机集合

.NET 8 DI GetServices<;对象&>不工作

.NET 6:如何防止系统生成的日志(log)?

未在Windows上运行的Maui项目

在平行内使用跨度.用于循环

流畅的验证--如何为属性重用规则?

如何读取TagHelper属性的文本值?

为什么连接到Google OAuth2后,结果.Credential为空?

当要删除的子模型没有父模型的1:多属性时,如何告诉实体框架设置1:1 FK条目?

在使用.NET EF Core DbContext属性之前,是否应使用null判断

如何更改Datagridview行标题