简体   繁体   English

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

[英]Why can't a C# struct method return a reference to a field, but a non-member method can?

Here's an example of an instance method of struct attempting to return a readonly ref to an instance field of the struct:下面是一个 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
}

This produces 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 However, doing the same thing using an extension method produces no error:但是,使用扩展方法做同样的事情不会产生错误:

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

Answers to the related question Why can't a C# structure return a reference to its member field?相关问题的答案为什么 C# 结构不能返回对其成员字段的引用? discuss the reasons why the language does not permit the first scenario, but given those reasons, it's not clear to me why the second scenario is allowed.讨论为什么语言不允许第一种情形的原因,但鉴于这些原因,为什么第二种情况允许它我不清楚。


Update:更新:

Here's a full example that does not use readonly , and also shows a non-extension method, and that demonstrates usage:这是一个不使用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
         */
    }
}

It's interesting that extension methods silently "add" ref , when normal calls require the caller to explicitly add ref to the argument, I presume to make it clear and prevent mistakes.有趣的是,扩展方法会默默地“添加” ref ,当正常调用需要调用者显式地将ref添加到参数时,我想说清楚并防止错误。

I think this is covered in ref-readonly proposal , Safe To Return Rules section.我认为这包含在ref-readonly 提案中,安全返回规则部分。

'this' is not safe to return from struct members从结构成员返回“this”是不安全的

This one you already know.这个你已经知道了。 You can read more about why it's not allowed here .您可以在此处阅读更多有关为什么不允许这样做的信息 In short - allowing it "contaminates any ref-returning method called on a local value type", so making all ref returns from methods on structs not "safe to return", because they might potentially contain ref to this.简而言之 - 允许它“污染任何在本地值类型上调用的 ref 返回方法”,因此使结构上的方法的所有ref 返回都不是“安全返回”,因为它们可能包含对 this 的引用。

ref/in parameters are safe to return ref/in 参数可以安全返回

instance struct fields are safe to return as long as the receiver is safe to return只要接收者可以安全返回,实例结构字段就可以安全返回

This covers static method case.这涵盖了静态方法案例。 foo parameter is safe to return (because in ), and therefore, foo._x is safe to return, being field of a struct instance which itself 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.如果作为形式参数传递给该方法的所有引用/输出都可以安全返回,则从另一个方法返回的引用是安全的。

This one prevents problems with the above static method.这可以防止上述静态方法出现问题。 It makes the following invalid:它使以下无效:

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);
}

Because s is not safe to return, ref we got from ExtensionGetX is not safe to return either, so we cannot leak pointer to local variable outside of scope.因为s不能安全返回,我们从ExtensionGetX得到的 ref 也不能安全返回,所以我们不能泄漏指向作用域外的局部变量的指针。

So in short - it's allowed because it's safe, and does not have a specific drawback which forbid returning ref to "this" from struct member method.所以简而言之 - 它是允许的,因为它是安全的,并且没有禁止从结构成员方法将 ref 返回到“this”的特定缺点。

Update.更新。 I don't think update to your question changes my answer.我认为对您问题的更新不会改变我的答案。 "safe to return" rules mentioned above stay the same.上面提到的“安全返回”规则保持不变。 You changed in to ref , but ref parameter is also safe to return.你改变inref ,但ref参数也安全返回。 So is its instance field.它的实例字段也是如此。 But if you make parameter not safe to return:但是如果你让参数不安全返回:

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

Then it won't compile.然后它不会编译。

You also think (in comment) that "you can't store the returned reference as a local variable", but that's not true:您还认为(在评论中)“您不能将返回的引用存储为局部变量”,但事实并非如此:

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

So, readonly or not, in or ref - safe to return rules ensure it's safe to return ref struct member in that situation, while allowing to return reference to this from struct local method has undesirable consequences, forcing to treat all ref values returned by all struct members as not safe to return.因此,无论是否只读, inref - 安全返回规则确保在这种情况下返回 ref struct 成员是安全的,同时允许从 struct local 方法返回对this引用会产生不良后果,强制处理所有返回的所有 ref 值struct 成员返回不安全。

Small example of what would not be possible if struct member can return ref to this :如果 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);
}

Another answer already explains why this compiler error is needed, and how it prevents you from accidentally keeping a dangling reference.另一个答案已经解释了为什么需要此编译器错误,以及它如何防止您意外保留悬空引用。

As a quick and dirty workaround, you can suppress this error by using unsafe fixed construct:作为一种快速而肮脏的解决方法,您可以使用unsafe fixed结构来抑制此错误:

struct Foo
{
    internal int _x;

    public unsafe ref readonly int MemberGetX()
    {
        fixed (int* ptr = &_x)
            return ref *ptr;
    }
}

But now it is your responsibility to make sure that references to fields of local or temporary instances of Foo do not escape their scope.但是现在您有责任确保对Foo的本地或临时实例的字段的引用不会超出其范围。 The compiler is no longer looking after you, and you are on your own.编译器不再照顾你,你就靠自己了。

But this approach only works for fields of an unmanaged type (ie primitive types and structs constructed entirely from them).但是这种方法只适用于非托管类型的字段(即完全由它们构造的原始类型和结构)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么C#结构不能返回对其成员字段的引用? - Why can't a C# structure return a reference to its member field? C#从方法返回结构引用 - C# return struct reference from method 为什么C#中的常量字段不能是非内置结构类型? - Why can't a constant field be of non-built-in struct type in C#? C# 方法可以返回方法吗? - Can a C# method return a method? 为什么 C# 不允许像 C++ 这样的非成员函数 - Why C# is not allowing non-member functions like C++ 为什么我在C#中得到“非静态字段,方法或属性需要对象引用”错误? - Why am I getting “object reference is required for the non-static field, method, or property” error in C#? 为什么我不能从 c# 中的 Powershell.Invoke() 方法返回所有 cimsession 函数 - Why can't i return all cimsession functions from Powershell.Invoke() method in c# C#7 - 为什么我不能从异步方法返回这个等待类型? - C# 7 - Why can't I return this awaitable type from an async method? 不能从匿名方法引用非静态方法 - can't reference non-static method from anonymous method 不可发音成员'xyz'不能像方法一样使用,vb和c#之间的区别 - Non-invocable member 'xyz' can not be used like a method, Difference between vb and c#
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM