C# Span<;T>;不需要局部变量赋值。这是一个特征吗?

C# Span<;T>;不需要局部变量赋值。这是一个特征吗?,c#,c#-7.2,system.memory,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) {} void Uniniti

我注意到,即使局部变量没有初始化,下面的代码也会编译和执行。这是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)
{}
void Uninitialized()
{
跨度s1;
var l1=s1.长度;
跨度s2;
未初始化输出(输出s2);
var l2=s2.长度;
}
无效未初始化数据(超出范围s)
{}

这或多或少是出于设计,因为它很大程度上取决于底层的
结构本身是否包含任何字段

此代码编译的示例如下:

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
{
公共整数长度=>1;
}
静态类程序
{
静态void Main(字符串[]参数)
{
MySpan-s1;
var l1=s1.长度;
}
}
但该代码不:

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

static class Program
{
    static void Main(string[] args)
    {
        MySpan<char> s1;
        var l1 = s1.Length;
    }
}
public struct MySpan
{
公共整数长度{get;}
}
静态类程序
{
静态void Main(字符串[]参数)
{
MySpan-s1;
var l1=s1.长度;
}
}

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

这看起来像是由引用程序集引起的问题,因为
Span
具有特定于框架的内部结构

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

如果所有字段都已赋值,则结构
被视为已赋值(为了“确定赋值”),在这种情况下,编译器会看到“所有零字段都已赋值:所有良好-此变量已赋值”。但是编译器似乎不知道实际的字段,所以它被误导,允许一些技术上无效的内容

你绝对不应该指望这个表现得很好!虽然在大多数情况下,
.locals init
应该意味着你不会得到太可怕的东西。然而,目前有一些工作正在进行中,以允许人们在某些情况下抑制
。locals init
——我不敢想象在这种情况下会发生什么——特别是因为
Span
的工作方式非常类似于
ref T
——如果字段真的没有初始化为零,那么可能会变得非常危险

有趣的是,它可能已经被修复了:参见。或者,sharplab可能正在使用一个具体的目标框架,而不是引用程序集


编辑:非常奇怪,如果我将参考程序集加载到
ildasm
或reflector中,我可以看到:

.field private initonly object _dummy
这是引用程序集中的伪造字段,用于阻止这种情况发生,但是。。。看起来它现在工作得不太可靠



更新:很明显,这里的区别是由于兼容性原因而存在的差异;结构的指定赋值考虑了本地已知类型的私有字段,但不考虑私有<强>引用类型< /强>外部程序集中的类型字段。

< P>贾景晖有一个很好的答案。我想详细说明一下历史/背景

首先,这绝对是一个错误。根据明确分配的规则,这个局部不是明确分配的,任何用法都应该是错误的。不幸的是,由于以下原因,此错误很难修复:

  • 这个bug很老了,至少可以追溯到C#4.0。这给了客户7年以上的时间来不经意间依赖它
  • BCL中有许多结构具有这种基本结构。比如说
这些加在一起意味着修复这一问题可能会破坏大量现有代码。尽管如此,C#团队还是试图在C#6.0中修复这个bug,当时这个bug还很年轻。但是,试图用这个补丁编译VisualStudio源代码的尝试表明,客户对这个bug的依赖性的担忧是有根据的:有很多构建中断。足以让我们相信它会对大量代码产生负面影响。因此,修复被撤消

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

仍在讨论这一点,但我们很可能会更新
Span
和其他类型的参考汇编策略,以避免此编译器错误


感谢您的报告。对于造成的混乱,我深表歉意:(

在我看来像个bug,但据我所知,
out
无论如何都不是CLR强制执行的。
Span
并不特别;您自己的
struct Foo{public int Length=>0;}
将触发此bug(或“次优处理”)还有。@jeroenmoster,这不是一个bug;
Foo
实际上没有字段,所以就明确的赋值而言:它是fine@MarcGravell:是--也不是。这是一件坏事:添加一个私有字段会破坏毫无戒心的代码,而这种客户端无法控制的脆弱性是不可取的。允许这样做看起来很糟糕它应该抱怨并强迫新的Foo()
(当然,还是编译成同样的东西),即使你可以解释为什么它不会,但根据规则,@Jeroemoster今天将一个公共或私有字段添加到一个结构中就可以做到这一点;将字段添加到结构中不是一个安全的更改。请考虑:
Position pos;pos.X=12;pos.Y=42;Draw(pos);
。这是一种用公共字段初始化可变结构的完全合法的方法(我现在将忽略带有公共字段的可变结构本身是否是好主意的问题…)。现在添加一个
Z
,您现有的代码将停止编译。我强烈怀疑