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

时间:2018-04-04 14:05:05

标签: 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)
{}

3 个答案:

答案 0 :(得分:17)

这似乎是由引用程序集引起的问题,因为Span<T>具有特定于框架的内部构件的方式,这是必需的。

这意味着在引用程序集中没有字段(编辑:这不是真的 - 请参阅脚注)。

如果分配了所有字段,则认为struct已分配(用于&#34;明确赋值&#34;),在这种情况下,编译器会看到&#34;零域的全部为零已被分配:所有好 - 这个变量被分配&#34;。但是编译器似乎并不了解实际的字段,所以它被误导为允许在技术上无效的东西。

你绝对不应该依赖这种行为!虽然在大多数情况下.locals init应该意味着你实际上并没有得到任何太多可怕的东西。但是, 目前正在进行一些工作以允许人们在某些情况下压制 .locals init - 我害怕想到中会发生什么< / em>这里的场景 - 特别是因为Span<T>的工作方式与ref T非常相似 - 如果字段真的不是那么非常会很危险em>初始化为零。

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

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

.field private initonly object _dummy

这是参考程序集中的欺骗字段意味着以阻止这种情况发生,但是......它看起来现在还不能非常可靠地工作!

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

答案 1 :(得分:15)

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

首先,这肯定是compiler bug。根据明确赋值的规则,本地未明确分配,任何使用都应该是错误。不幸的是,由于多种原因,这个bug很难解决:

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

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

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

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

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

答案 2 :(得分:1)

这或多或少是设计的,因为如果基础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的回答中所解释的那样。