[英]Why can't a C# struct method return a reference to a field, but a non-member method can?
下面是一个 struct 的实例方法的例子,它试图返回一个只读 ref 到结构的实例字段:
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 Struct members cannot return 'this' or other instance members by reference 。 但是,使用扩展方法做同样的事情不会产生错误:
static class FooExtensions
{
public static ref readonly int ExtensionGetX( this in Foo foo )
{
return ref foo._x;
}
}
相关问题的答案为什么 C# 结构不能返回对其成员字段的引用? 讨论为什么语言不允许第一种情形的原因,但鉴于这些原因,为什么第二种情况是允许它我不清楚。
更新:
这是一个不使用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 提案中,安全返回规则部分。
从结构成员返回“this”是不安全的
这个你已经知道了。 您可以在此处阅读更多有关为什么不允许这样做的信息。 简而言之 - 允许它“污染任何在本地值类型上调用的 ref 返回方法”,因此使结构上的方法的所有ref 返回都不是“安全返回”,因为它们可能包含对 this 的引用。
ref/in 参数可以安全返回
只要接收者可以安全返回,实例结构字段就可以安全返回
这涵盖了静态方法案例。 foo
参数可以安全返回(因为in
),因此, foo._x
可以安全返回,它是本身可以安全返回的结构实例的字段。
如果作为形式参数传递给该方法的所有引用/输出都可以安全返回,则从另一个方法返回的引用是安全的。
这可以防止上述静态方法出现问题。 它使以下无效:
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 也不能安全返回,所以我们不能泄漏指向作用域外的局部变量的指针。
所以简而言之 - 它是允许的,因为它是安全的,并且没有禁止从结构成员方法将 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 成员是安全的,同时允许从 struct local 方法返回对this
引用会产生不良后果,强制处理所有返回的所有 ref 值struct 成员返回不安全。
如果 struct member 可以将 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);
}
另一个答案已经解释了为什么需要此编译器错误,以及它如何防止您意外保留悬空引用。
作为一种快速而肮脏的解决方法,您可以使用unsafe
fixed
结构来抑制此错误:
struct Foo
{
internal int _x;
public unsafe ref readonly int MemberGetX()
{
fixed (int* ptr = &_x)
return ref *ptr;
}
}
但是现在您有责任确保对Foo
的本地或临时实例的字段的引用不会超出其范围。 编译器不再照顾你,你就靠自己了。
但是这种方法只适用于非托管类型的字段(即完全由它们构造的原始类型和结构)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.