Span <T>不需要局部变量赋值.这是一个功能吗?

jyo*_*ung 14 c# c#-7.2 system.memory

我注意到,即使未初始化局部变量,以下内容也将编译并执行.这是Span的一个特色吗?

void Uninitialized()
{
  Span<char> s1;
  var l1 = s1.Length;

  Span<char> s2;
  UninitializedOut(out s2);
  var l2 = s2.Length;
}

void UninitializedOut(out Span<char> s)
{}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 18

这看起来像是由引用程序集引起的问题,因为Span<T>具有特定于框架的内部构件的方式.

这意味着在引用程序集中:没有字段(编辑:这不完全正确 - 请参阅脚注).

struct如果分配了所有字段,则认为A 被分配(出于"明确赋值"的目的),并且在这种情况下,编译器看到"已经分配了零字段的全部零:所有好的 - 分配了该变量".但编译器似乎并不知道实际的字段,因此它被误导为允许在技术上无效的东西.

你绝对不应该依赖这种行为!虽然在大多数情况下.locals init应该意味着你实际上并没有可怕.然而,目前正在进行的一些工作,让人们抑制 .locals init在某些情况下-我不敢想会发生什么,情景在这里-尤其是因为Span<T>作品很像ref T-这可以得到非常非常危险的,如果现场真的是不初始化为零.

有趣的是,它可能已经修复:请参阅sharplab上的这个示例.或者,也许sharplab使用的是具体的目标框架,而不是参考组件.


编辑:非常奇怪,如果我将参考组件加载到ildasm反射器中,我可以看到:

.field private initonly object _dummy
Run Code Online (Sandbox Code Playgroud)

这是参考组件中的欺骗字段,旨在阻止这种情况发生,但......现在看起来它不能非常可靠地工作!


更新:显然这里的差异是一个微妙但已知的编译器问题,仍然是出于兼容性原因; 结构的明确赋值考虑本地已知类型的私有字段,但不考虑外部程序集中类型的私有引用类型字段.


Jar*_*Par 15

马克有一个很好的答案.我想详细说明一下历史/背景.

首先,这绝对是一个编译器错误.根据明确赋值的规则,本地未明确分配,任何使用都应该是错误.不幸的是,由于多种原因,这个bug很难解决:

  • 这个bug很老,至少可以追溯到C#4.0.这让客户在7年多的时间里无意中依赖它
  • BCL中有许多具有这种基本结构的结构.例如CancellationToken.

这些合在一起意味着修复这可能会破坏大量现有代码.尽管如此,C#团队还是试图修复C#6.0中的bug,因为它的bug要小得多.但尝试使用此修复程序编译Visual Studio源时,表明围绕客户依赖此错误的担忧是有根据的:有许多构建中断.足以让我们相信它会对大量代码产生负面影响.因此解决方案已撤消.

这里的第二个问题是所有编译器团队成员都不知道这个错误(至少在今天之前).距离修复工作已经3年了,从那时起就有了一些转变.验证我们如何生成参考组件的团队成员Span<T>没有发现此错误,并根据语言规范推荐了当前的设计.我是那些开发者之一:(

仍在讨论这个问题,但我们很可能会更新Span<T>和其他类型的引用程序集策略,以避免这个编译器错误.

感谢您报告此事.抱歉造成的混乱:(