【问题标题】:Span<T> can not be a nested local variable. Why is this a restriction?Span<T> 不能是嵌套的局部变量。为什么这是一个限制?
【发布时间】:2018-09-14 04:49:07
【问题描述】:

以下内容无法编译。既然这不是匿名方法、lambda 表达式或查询表达式,为什么这是一个限制? (将文本作为 ref ReadOnlySpan 工作正常。)

    void TestNestedSpan()
    {
        var text = "Some text".AsReadOnlySpan();
        var success = TryParseS();

        bool TryParseS()
        {
            //Compile Error CS8175 Cannot use ref local 'text' inside 
            //  an anonymous method, lambda expression, or query expression
            if (text.Length < 1 || text[0] != 'S') 
                return false;

            text = text.Slice(1);
            return true;
        }
    }

【问题讨论】:

  • 你觉得TryParseS是怎么编译的? CLR 并没有真正的嵌套函数。都是编译器撒的谎。
  • @JeroenMostert 公平地说,您不必担心这一点。如果编译器告诉您不能在匿名方法、lambda 表达式或查询表达式中使用某个功能,那么当您不在匿名方法、lambda 表达式或查询表达式中使用该功能时,这听起来像是一个编译器错误我。错误消息应提及本地函数。
  • @hvd:我正要改变我的评论,提到诊断可能应该被修改——但现在我不会了。 :-)
  • 修改诊断仍然会给我留下“为什么这是一个限制?”。
  • @RandRandom 是关于NuGet包System.Memory中定义的类型和扩展方法的问题。

标签: c# c#-7.2 system.memory


【解决方案1】:

编译器消息不完整,还应提及本地函数。

修改诊断仍然会让我想“为什么这是一个限制?”。

本地函数不能从外部范围引用类似 ref 的结构,因为本地函数不需要在本地调用。

Action a;

void f() {
    var s = "Hello, world!\n".AsReadOnlySpan();
    void g() {
        foreach (var c in s)
            Console.Write(c);
    }
    a = g;
}

void h() {
    a(); //call a which is in fact the "local" method g in f
}

你提到:

(将文本作为 ref ReadOnlySpan 工作正常。)

这是正确的,但这会改变方法的签名。如果编译器这样做,就不能再将本地函数作为委托类型传递。


如 cmets 中所述:

本地函数的不同翻译可能实际上允许这样做,它只是不允许将这样的本地函数分配给Action

Action a;

ref struct S {
    public ReadOnlySpan<char> s;
    public void g() {
        foreach (var c in s)
            Console.Write(c);
    }
}

void f() {
    S s;
    s.s = "Hello, world!\n".AsReadOnlySpan();
    s.g(); // okay
    a = s.g; // error
}

作为 C# 的未来扩展,这实际上可能是一个好主意。但此时,它还没有被设计或实现。在允许这样做之前,需要考虑几种极端情况,例如当有多个本地函数在捕获中存在一些重叠时。

【讨论】:

  • 通过调用 a 的示例扩展您的答案,希望没问题。
  • @RandRandom 这种编辑通常最好先在评论中提出建议,但如果你这样做了,是的,我可能会添加它。
  • 是的,我现在看到共享的父局部变量是在堆上分配的,而不是像 Pascal 这样的堆栈。
  • 我想说这与它可能被分配给代表的事实无关。这是因为Span&lt;T&gt; 和朋友是“仅堆栈”类型,并且各种功能在 C# 中的工作方式是它们生成带有字段的隐藏类型以捕获任何本地状态。同样的限制适用于异步或迭代器方法。它们不能有Span&lt;T&gt;(或ReadOnlySpan&lt;T&gt;)类型的参数或局部变量。
  • 我想我正在将此与本地函数中标准捕获变量的情况进行比较。如果没有委托分配,本地函数的捕获变量将分配给值类型字段并作为 by ref 参数传递。通过委托分配,这完全改变了。在我看来,如果编译器没有分配给一个字段,编译器可以完全按照需要做的事情来做。
猜你喜欢
  • 2018-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多