在方法中创建结构时,Ref return 不起作用

Ref return doesn't work when struct is created in the method

为什么这行不通?
我创建了结构并希望通过 ref.

return 它
public readonly struct AuditResult
{
    public readonly bool AcceptChanges;
    public readonly string Reason;

    public AuditResult(bool acceptChanges, string reason)
    {
        AcceptChanges = acceptChanges;
        Reason = reason;
    }

    public static ref AuditResult AcceptAuditResult()
    {
        var auditResult = (new AuditResult(true, string.Empty));
        ref AuditResult res = ref auditResult;

        return ref res;
    }
}

发生此错误:

CS8157 Cannot return 'res' by reference because it was initialized to a value that cannot be returned by reference

在这种情况下,我的变量是 ref

C# documentation 指出:

The return value must have a lifetime that extends beyond the execution of the method. In other words, it cannot be a local variable in the method that returns it. It can be an instance or static field of a class, or it can be an argument passed to the method. Attempting to return a local variable generates compiler error CS8168, "Cannot return local 'obj' by reference because it is not a ref local."

为了使用 return ref 关键字,您需要 return 一个 而不是 局部变量的对象,因为局部变量将超出范围并被垃圾收集。相反,考虑 return 对 class/struct 中的成员变量的引用,而不是 res。此外,请考虑是否需要通过引用 return 值 - 如果您不在内部其他地方访问它,则无需通过引用传递它。


但是请注意,您 可以 在本地使用 ref 关键字为局部变量名称创建别名,如下所示:

Foo a = new Foo();
ref Foo b = ref a;

这里,修改b也会修改a。但是,使用此语法,您无法在当前方法范围之外传递本地引用。

这是 C++ 或 Rust 等非托管语言中的常见错误 - 通过使用 ref 语义,您可以在决定管理自己的内存时有效地编写代码。

考虑以下 C++ 位:

char* Greet(char* name)
{
  char buffer[100];
  sprintf(buffer, "Hello, %s!", name);
  return buffer;
}

方法退出后,buffer 超出作用域 - 它不再存在,您将留下一个悬空指针。这个模式叫做 RAII - Resource acquisition is initialization.

你正在尝试做类似的事情 -

ref var something = ...
return ref res;

该局部变量超出范围 - 您将尝试 return 引用不存在的内容。

但是为什么 ref return 首先存在?

通过引用返回意味着只复制引用,不复制结构。 有时,创建一个新结构可能代价高昂。

static readonly AuditResult AcceptWithNoReason = 
    new AuditResult(true, string.Empty);

public static ref readonly AuditResult AcceptAuditResult()
{
    return ref AcceptWithNoReason; //this is valid
}

但是您确实必须 return 对生命周期比该方法调用更长的东西的引用。阅读更多 here.