[英]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.