下面是一个结构实例方法的例子,它试图将只读引用返回给结构的实例字段:
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”的特定缺点。
更新。我认为更新您的问题不会改变我的答案。上述“安全返回”规则保持不变。你改变了in
至 ref
,但是 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);
所以,不管是否只读,in
或 ref
- 安全返回规则确保在这种情况下返回 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/