ref关键字在C#中返回类型之前的含义是什么

Cod*_*shi 4 c#

在下面的代码是什么的意义refGetAge()方法签名?

public class Person
{
    private int age;
    public ref int GetAge()
    {
        return ref this.age;
    }
}
Run Code Online (Sandbox Code Playgroud)

Cod*_*shi 8

ref回报是在C#7.0中的新功能.它允许返回对内存位置的引用.在以前的C#版本中这是不可能的.您甚至可以存储返回的内存位置,如下所示:

var person = new Person();

// Here we can store the reference to the memory area and we can modify it
ref int age = ref person.GetAge();

// like this
age = 50;
Run Code Online (Sandbox Code Playgroud)

我们正在处理相同的内存位置,而不是整个时间复制age.


幕后发生了什么?

如果我们有这个代码:

public class Program
{
    public static void Main()
    {
        var person = new Person();

        // Here we can store the reference to the memory area and we can modify it
        ref int age = ref person.GetAge();

        // like this
        age = 50;
    }
}

public class Person
{
    private int age;
    public ref int GetAge()
    {
        return ref this.age;
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是编译器(Roslyn)为该代码幕后操作的内容:

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
[assembly: AssemblyVersion("0.0.0.0")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[module: UnverifiableCode]
public class Program
{
    public unsafe static void Main()
    {
        Person person = new Person();
        int* age = person.GetAge();
        *age = 50;
    }
}
public class Person
{
    private int age;
    public unsafe int* GetAge()
    {
        return ref this.age;
    }
}
Run Code Online (Sandbox Code Playgroud)

好!!!我想我们都很高兴我们不必处理所有那些*恶作剧.


这个功能什么时候有用?

通过避免复制值或多次执行解除引用操作,添加ref本地和ref返回使算法更有效.

当您使用值类型(struct)的大型数据结构并将副本传入和传出方法可能不是非常有效时,它最有用.例如,假设我们有一个包含一堆struct对象的类:

class Container
{
    private Tile[] tiles = new Tile[] { new Tile { X = 10 } };

    public Tile this[int x]
    {
        get { return tiles[x]; }
        set { tiles[x] = value; }
    }
}

public struct Tile
{
    public int X { get; set; }
    // Many more propeties
}
Run Code Online (Sandbox Code Playgroud)

如果我们想要处理Tile对象,因为它们是struct,我们将无法做到这一点:

var container = new Container();
container[0].X = 10;
Run Code Online (Sandbox Code Playgroud)

我们不能这样做,因为编译器会发出此错误:

错误CS1612无法修改'Container.this [int]'的返回值,因为它不是变量

编译器抛出该错误以明确表明您认为自己正在做的事情(修改索引项),并不是您正在做的事情.您实际上正在修改副本,因此它会强制您执行此操作.因此,为了能够设置X,您需要在副本上执行此操作:

var container = new Container();
var copy = container[0];
copy.X = 10;

// now we need to set the item to the copy
container[0] = copy;
Run Code Online (Sandbox Code Playgroud)

正如您所看到的那样效率不高,特别是如果我们正在使用大型工作,struct我们需要以迭代方式操作其中的许多工作.

使用C#7.0,我们可以这样做:

public ref Tile this[int x]
{
    get { return ref tiles[x]; }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以Tile直接操作s而无需发送副本,制作副本,然后将原始项目设置为副本.像这样:

var container = new Container();
ref Tile tile = ref container[0];  
tile.X = 10;  
Run Code Online (Sandbox Code Playgroud)

一个小问题

网上有很多例子,它们的语法如下:

// Notice the ref missing on the right side
ref int age = person.GetAge();
Run Code Online (Sandbox Code Playgroud)

这将导致此错误:

无法使用值初始化by-reference变量

正确的语法是ref双方都这样:

ref int age = ref person.GetAge();
Run Code Online (Sandbox Code Playgroud)

更多信息

是一个SO问题,其中已经讨论了该特征.我想这个问题现在已成为历史.而这里是另一篇文章的埃里克利珀有关此功能.