“scoped”关键字的目的是什么

Twe*_*nty 19 c# keyword c#-11.0

在查看 new 的源代码时,DefaultInterpolatedStringHandler我注意到 是ReadOnlySpanscoped关键字注释的。我能找到的唯一文档是here。但是,我无法弄清楚以下代码片段之间的实际区别是什么。我假设使用scoped关键字,参数不允许传递给被调用的方法,也不允许返回。即使事实证明这是真的,它有什么实际用途呢?

public void AppendFormatted(ReadOnlySpan<char> value) 
{
  // Omitted for brevity
}

// vs

public void AppendFormatted(scoped ReadOnlySpan<char> value) 
{
  // Omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*d L 22

正如我在评论中提到的,引用结构参数关键字的目的scoped是允许将堆栈分配的局部变量传递给不捕获入站引用结构或从方法返回入站引用结构的方法。

在以下您链接的来源的非常简单的示例中:

public ref struct Test
{
    private Span<char> _chars;
    private int _pos;
    
    public Test()
    {
        _chars = new char[3];   
        _pos = 0;
    }
    
    public void AppendFormatted(ReadOnlySpan<char> value)
    {
        // Fast path for when the value fits in the current buffer
        if (value.TryCopyTo(_chars.Slice(_pos)))
        {
            _pos += value.Length;
        }
    }
    
    public Span<char> GetBuffer() => _chars.Slice(0, _pos);
}
Run Code Online (Sandbox Code Playgroud)

如果从以下位置调用:

RunSpan();

void RunSpan()
{
    var test = new Test();
    Span<char> valuesToCopy = stackalloc char[1] { 'd' };
    test.AppendFormatted(valuesToCopy);
}
Run Code Online (Sandbox Code Playgroud)

它会抛出以下错误

错误CS8352:无法在此上下文中使用变量“valuesToCopy”,因为它可能会在其声明范围之外公开引用的变量错误

CS8350:不允许使用“Test.AppendFormatted(ReadOnlySpan)”的这种参数组合,因为它可能会暴露参数“value”引用的变量超出其声明范围

但是,一旦添加关键字,scopedAppendFormatted(scoped ReadOnlySpan<char> value)就可以调用该方法。

请注意,作用域不允许您从方法中错误地返回参数!下列:

public ref struct Test
{
    private Span<char> _chars;
    private int _pos;
    
    public Test()
    {
        _chars = new char[3];   
        _pos = 0;
    }
    
    public ReadOnlySpan<char> AppendFormattedBad(scoped ReadOnlySpan<char> value) => value;
    
    public Span<char> GetBuffer() => _chars.Slice(0, _pos);
}
Run Code Online (Sandbox Code Playgroud)

仍然返回以下错误:

错误CS8352:无法在此上下文中使用变量“ReadOnlySpan”,因为它可能会在其声明范围之外公开引用的变量

在处理 ref 结构时,此功能非常有用,因为您经常需要 stackalloc 传递给 ref 结构的方法的缓冲区。

  • @NickStrupat,这是因为构造函数会自动为缓冲区的生命周期创建一种“范围”感……它不能存在于“Test”实例本身之外。但是,当将其传递给方法时,编译器不再能够安全地推断缓冲区的生命周期。该方法有可能将 ref 结构存储在方法调用的堆栈之外,并且它完全不允许这种使用。“scoped”现在允许我们告诉编译器该方法的行为方式与构造函数相同。 (4认同)
  • 这很有趣。您的示例演示了这个问题,但是如果我将构造函数更改为 `public Test(Span&lt;char&gt; chars) { _chars = chars; ... }`,不需要编译作用域。我不明白为什么。你知道吗?谢谢! (2认同)