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

Span<T> does not require local variable assignment. Is that a feature?

我注意到即使局部变量没有初始化,下面的代码也会编译和执行。这是 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)
{}

这或多或少是设计使然,因为它在很大程度上取决于基础 struct 本身是否包含任何字段。

此代码编译例如:

public struct MySpan<T>
{
    public int Length => 1;
}

static class Program
{
    static void Main(string[] args)
    {
        MySpan<char> s1;
        var l1 = s1.Length;
    }
}

但是这段代码没有:

public struct MySpan<T>
{
    public int Length { get; }
}

static class Program
{
    static void Main(string[] args)
    {
        MySpan<char> s1;
        var l1 = s1.Length;
    }
}

似乎在那种情况下,结构是默认的,这就是为什么它不抱怨缺少赋值的原因。正如 Marc 的回答中所解释的,它没有检测到任何字段 一个错误。

这看起来像是引用程序集引起的问题,由于 Span<T> 具有 framework-specific 内部结构的方式而需要。

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

如果所有字段都已分配,则

A struct 被视为已分配(出于 "definite assignment" 的目的),在这种情况下,编译器会看到 "all zero of zero fields have been assigned: all good - this variable is assigned"。但是编译器似乎并不知道 actual 字段,因此它被误导允许某些技术上无效的内容。

你绝对不应该依赖这个表现得很好!虽然在大多数情况下 .locals init 应该意味着你实际上并没有得到任何 可怕的东西。但是, 目前正在进行一些工作,以允许人们在某些情况下 suppress .locals init - 我不敢想象会发生什么在这里的场景中——特别是因为Span<T>的工作方式很像ref T——如果字段确实没有初始化为零。

有趣的是,它 可能已经修复 :参见 this example on sharplab。或者,也许 sharplab 使用的是具体的目标框架,而不是参考程序集。


编辑:很奇怪,如果我将参考程序集加载到 ildasm 或反射器中,我可以看到:

.field private initonly object _dummy

这是参考程序集中的欺骗字段,旨在 阻止这种情况发生,但是......看起来它现在工作得不是很可靠!


更新:显然这里的区别是 subtle but known compiler issue 出于兼容性原因而保留;结构的明确赋值考虑本地已知类型的私有字段,但不考虑私有reference-type 外部程序集中的类型字段。

Marc 的回答很棒。我想详细说明一下历史/背景。

首先这绝对是一个compiler bug。根据明确赋值的规则,这个局部变量不是明确赋值的,任何用法都应该是错误的。不幸的是,这个错误很难修复,原因有很多:

  • 这个错误是旧的,至少可以追溯到 C# 4.0。这让客户有 7 年以上的时间不经意地依赖它
  • BCL 中有许多结构体都具有这种基本结构。例如 CancellationToken.

这些加在一起意味着解决这个问题可能会破坏大量现有代码。尽管如此,C# 团队还是尝试在 C# 6.0 中修复该错误,当时该错误还很年轻。但是尝试使用此修复程序编译 Visual Studio 源代码表明,围绕客户依赖此错误的担忧是有根据的:存在许多构建中断。足以让我们相信它会对大量代码产生负面影响。因此修复被撤消。

这里的第二个问题是所有编译器团队成员都不知道这个错误(至少在今天之前)。自从修复被撤消以来已经过去了大约 3 年,从那以后有了一些转变。验证我们如何为 Span<T> 生成参考程序集的团队成员并不知道这个错误,并根据语言规范推荐了当前的设计。我是那些开发人员之一:(

仍在讨论这个问题,但很可能我们将更新 Span<T> 和其他类型的参考汇编策略,以避免此编译器错误。

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