c# - 为什么 C# 结构方法不能返回对字段的引用,但非成员方法可以?

标签 c#

下面是一个结构实例方法的例子,它试图将只读引用返回给结构的实例字段:

struct Foo
{
    internal int _x;

    public ref readonly int MemberGetX() => ref _x;
    //                                          ^^^
    // Error CS8170: Struct members cannot return 'this' or other instance members by reference
}

这会产生错误 CS8170 结构成员无法通过引用返回“this”或其他实例成员。然而,使用扩展方法做同样的事情不会产生错误:

static class FooExtensions
{
    public static ref readonly int ExtensionGetX( this in Foo foo )
    {
        return ref foo._x;
    }
}

相关问题的答案 Why can't a C# structure return a reference to its member field?讨论语言不允许第一种情况的原因,但考虑到这些原因,我不清楚为什么第二种情况是允许的。


更新:

这里有一个完整的例子,它不使用readonly,也显示了一个非扩展方法,并演示了用法:

struct Foo
{
    internal int _x;

    // Can't compile, error CS8170
    // public ref int GetXRefMember() => ref _x;

    public int X => _x;
}

static class FooExtensions
{
    public static ref int GetXRefExtension( this ref Foo foo )
    {
        return ref foo._x;
    }

    public static ref int GetXRef( ref Foo foo )
    {
        return ref foo._x;
    }
}

class Program
{
    static void Main( string[] args )
    {
        var f = new Foo();
        Console.WriteLine( f.X );
        f.GetXRefExtension() = 123;
        Console.WriteLine( f.X );

        // You can also do it without using an extension method, but the caller is required to specify ref:
        FooExtensions.GetXRef( ref f ) = 999;
        Console.WriteLine( f.X );

        /* Output:
         * 0
         * 123
         * 999
         */
    }
}

有趣的是,扩展方法默默地“添加”ref,当正常调用要求调用者显式添加 ref 到参数时,我假设要明确并防止错误。

最佳答案

我认为这包含在 ref-readonly proposal 中, 安全返回规则部分。

'this' is not safe to return from struct members

这个你已经知道了。您可以阅读更多关于为什么不允许的信息 here .简而言之 - 允许它“污染任何在本地值类型上调用的引用返回方法”,因此使所有 ref 从结构上的方法返回不是“安全返回”,因为它们可能包含引用到这个。

ref/in parameters are safe to return

instance struct fields are safe to return as long as the receiver is safe to return

这涵盖了静态方法的情况。 foo返回参数是安全的(因为 in ),因此 foo._x返回是安全的,作为本身可以安全返回的结构实例的字段。

a ref, returned from another method is safe to return if all refs/outs passed to that method as formal parameters were safe to return.

这可以防止上述静态方法出现问题。它使以下内容无效:

public static ref readonly int ExtensionGetX(in Foo foo) {
    return ref foo._x;
}

static ref readonly int Test() {
    var s = new Foo();
    s._x = 2;
    // fails to compile
    return ref ExtensionGetX(s);
}

因为 s返回是不安全的,引用我们从 ExtensionGetX 得到的返回也不安全,因此我们不能将指向局部变量的指针泄漏到范围之外。

简而言之 - 它是允许的,因为它是安全的,并且没有禁止从结构成员方法返回 ref 到“this”的特定缺点。

更新。我认为更新您的问题不会改变我的答案。上述“安全返回”规则保持不变。你改变了inref ,但是 ref参数也可以安全返回。它的实例字段也是如此。但是,如果您使返回的参数不安全:

public static ref int GetXRef(Foo foo)
{
    return ref foo._x;
}

那么它不会编译。

您还认为(在评论中)“您不能将返回的引用存储为局部变量”,但事实并非如此:

ref int y = ref FooExtensions.GetXRef(ref f);
y = 10;
// print 10
Console.WriteLine(f.X);

所以,不管是否只读,inref - 安全返回规则确保在这种情况下返回 ref struct 成员是安全的,同时允许返回对 this 的引用from struct local 方法会产生不良后果,强制将所有 struct 成员返回的所有 ref 值视为不安全返回。

如果 struct 成员可以将 ref 返回给 this 则不可能发生什么的小例子:

public ref int GetXRefMember(ref int y) => ref y;

static ref int Test(ref int y) {
    var x = new Foo();
    // safe to return, but won't be if ref to `this` can
    // ever be returned from struct local method
    return ref x.GetXRefMember(ref y);
}

关于c# - 为什么 C# 结构方法不能返回对字段的引用,但非成员方法可以?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50490143/

相关文章:

c# - 使用c#从另一个进程调用一个函数

c# - 跨域基本认证

c# - 视觉风格独立绘图

c# - 尝试在 C# 中更改工作表名称时出现奇怪的错误

c# - 如何获取RowUpdating事件GridView中的单元格值?

c# - Entity Framework 代码第一个链接表中违反 PRIMARY KEY 约束

c# - 关联方法

c# - 基于两个属性从列表中选择不同值的最快方法

c# - 截断两位小数而不四舍五入

c# - LargeImages 到 WPF ListView 端口