C# 11 ref 参数的转义规则:ref int 与 Span<int>

Bar*_*osz 6 c# ref c#-11.0

为什么以下代码在 C# 11 中无法编译?

// Example 1 - fails
class C {
    public Span<int> M(ref int arg) {
        Span<int> span;
        span = new Span<int>(ref arg);
        return span;
    }
}
Run Code Online (Sandbox Code Playgroud)

它产生两个编译错误:

错误CS9077:无法通过 ref 参数通过引用“arg”返回参数;它只能在 return 语句中返回。

错误 CS8347:无法在此上下文中使用“Span.Span(ref int)”的结果,因为它可能会在其声明范围之外公开参数“reference”引用的变量。

它们对我来说都没有意义:我的代码不会尝试arg通过 ref 参数返回,并且它不能公开arg其声明范围之外引用的变量。

对比之下,下面两段代码编译成功:

// Example 2 - succeeds
class C {
    public Span<int> M(ref int arg) {
        Span<int> span = new Span<int>(ref arg);
        return span;
    }
}
Run Code Online (Sandbox Code Playgroud)
// Example 3 - succeeds
class C {
    public Span<int> M(Span<int> arg) {
        Span<int> span;
        span = new Span<int>(ref arg[0]);
        return span;
    }
}
Run Code Online (Sandbox Code Playgroud)

我的直觉是,Span<int>内部保存了一个 ref 类型的字段int,因此转义规则对于上面的示例 1 和 3 应该是相同的(显然,它们不是)。

我使用显式保存 ref 字段的 ref 结构进行了类似的实验:

ref struct S {
    public ref int X;
}
Run Code Online (Sandbox Code Playgroud)

现在,以下代码无法编译:

// Example 4 - fails
class C {
    public S M(ref int arg) {
        S instance;
        instance.X = ref arg;
        return instance;
    }
}
Run Code Online (Sandbox Code Playgroud)

它会产生以下错误,这至少对我来说更有意义:

错误 CS9079:无法将“arg”引用分配给“X”,因为“arg”只能通过 return 语句转义当前方法。

对比之下,下面两段代码编译成功(具有上面的定义S):

// Example 5 - succeeds
class C {
    public S M(ref int arg) {
        S instance = new S() { X = ref arg };
        return instance;
    }
}
Run Code Online (Sandbox Code Playgroud)
// Example 6 - succeeds
class C {
    public S M(S arg) {
        S instance;
        instance.X = ref arg.X;
        return instance;
    }
}
Run Code Online (Sandbox Code Playgroud)

特别是,如果只能通过 return 语句转义当前方法,如上面示例 4 的错误消息所示,而示例 6 中arg不也是如此吗?arg.X

我试图在低级结构改进的文档中找到答案,但失败了。此外,该文档页面似乎在几个地方自相矛盾。

小智 0

你确定你使用的是 C# 11 吗?将 linqpad 与 .Net 7 一起使用,您的“编译失败”示例对我来说效果很好:

编译良好

更新:如果使用 Rosyln 编译器的每日构建,则无法编译

我的新假设是规范实际上变得更严格......并且 ex1 和 ex2 都应该失败,但是他们没有考虑到 ex2 语法,它在应该触发的时候没有触发(出于 Marc G 指出的原因)所以可能值得就此提交错误报告:-)