The ref
return 是 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;
整个过程中我们都在同一个内存位置上工作,而不是在同一个内存位置上工作。copy of the 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;
}
}
以下是编译器 (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;
}
}
好的!!!我想我们都很高兴我们不必处理所有这些*
恶作剧。
这个功能什么时候有用?
添加 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
}
如果我们想与Tile
对象,因为它们是struct
,我们将无法这样做:
var container = new Container();
container[0].X = 10;
我们不能这样做,因为编译器会发出以下错误:
错误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;
正如您所看到的,这不是很有效,特别是当我们与大型企业合作时struct
我们需要以迭代的方式操纵其中许多。
使用 C# 7.0,我们可以这样做:
public ref Tile this[int x]
{
get { return ref tiles[x]; }
}
现在我们可以操纵Tile
直接进行,无需发送副本、制作副本,然后将原始项目设置为副本。像这样:
var container = new Container();
ref Tile tile = ref container[0];
tile.X = 10;
一个小问题
网上有很多例子,它们的语法如下:
// Notice the ref missing on the right side
ref int age = person.GetAge();
这将导致此错误:
无法使用值初始化按引用变量
正确的语法是ref
两边都是这样的:
ref int age = ref person.GetAge();
更多信息
Here是一个 SO 问题,其中已经讨论了此功能。我想这个问题现在是history. And here是 Eric Lippert 撰写的关于此功能的另一篇文章。