[译]C# 7系列,Part 7: ref Returns ref返回结果
原文:https://blogs.msdn.microsoft.com/mazhou/2017/12/12/c-7-series-part-7-ref-returns/
背景
有两种方法可以将一个值传递给一个方法:
- 按值传递。当一个参数被传递给一个方法时,一个参数的副本(如果它是一个值类型)或一个"参数引用"的副本(如果它是一个引用类型)被传递。当您更改方法中的参数时,更改(单个赋值或复合赋值)会反映到参数/"参数引用"的副本,而不会反映到参数或“参数引用”本身。这是.NET语言中的默认方式(Visual Basic中的ByVal)。(译注:"参数引用"其实是个指针,指向另外一个它实际代表的对象,这里指的是修改这个指针本身。)
- 以引用的方式传递。当一个参数被传递给一个方法时,要么参数本身(如果它是一个值类型)要么“参数引用”(如果它是一个引用类型)被直接传递。没有生成其他副本。当您更改方法中的参数时,更改(单个赋值或复合赋值)将反映到参数或“参数引用”本身。这可以通过在C#中使用ref关键字或者在Visual Basic中使用ByRef关键字来表明。
例如,FCL(.NET Framework Class Library)中的Array. resize()方法接受类型为Array的ref参数,该ref值在方法实现中进行了修改,以指向调整大小后的数组新空间。你可以继续使用该数组变量,并访问新的内存空间:
byte[] data = new byte[];
Array.Resize(ref data, ); //译注:上一行data指向的内存空间和这里data指向的内存空间可能变更了。
Console.WriteLine($"New array size is: {data.Length}");
ref返回结果
ref返回结果是方法的引用返回值。与作为方法参数传递的ref值类似,调用者可以修改ref返回值,对该返回值的任何更改(赋值)都将反映到从方法中返回的原始值。
在C#中,你可以使用return ref关键字创建一个ref返回结果。
请看下面的示例:
private static ref T ElementAt<T>(ref T[] array, int position)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
} if (position < || position >= array.Length)
{
throw new ArgumentOutOfRangeException(nameof(position));
} return ref array[position];
}
上述方法的目的是在数组的特定位置获取对元素的引用。稍后你可以使用这个引用来更改该元素的值;因为它是一个ref值,所以更改将应用于数组中的原始值。(译注:就是数组中的原始值会被改变)
要使用这个方法,需要使用ref局部变量:
private static void Main(string[] args)
{
int[] data = new int[];
Console.WriteLine($"Before change, element at 2 is: {data[2]}");
ref int value = ref ElementAt(ref data, );
// Change the ref value.
value = ;
Console.WriteLine($"After change, element at 2 is: {data[2]}");
}
Visual Studio智能感知表面调用的方法是一个ref返回结果方法。
![]()
上面代码的运行输出结果如下:
![]()
调用带有ref返回结果的方法
与前面的示例一样,要获得ref返回值的引用,你需要使用ref局部变量,并将ref关键字放在调用方法前面(ref在等式左右两边都有)。
ref int value = ref ElementAt(ref data, );
你还可以在不使用ref关键字的情况下调用此方法,这时候返回的是值。(译注:不是引用)
int value = ElementAt(ref data, );
在这种情况下,程序输出如下:
![]()
但是,你需要在两边都指定ref,或者两边都没有ref;你不能在一边指定ref而在另一边不指定ref。
此外,以下代码也可以运行:
ElementAt(ref data, ) = ;
你将获得与第一个示例相同的输出。位置2的元素从0(默认值)更改为5。这是因为ref返回可以作为一个LValue出现。(译注:LValue被称为左值,简单地说就是出现在等号左边的值,详见"左值和右值表达式")
解决重载
因为返回类型不是认定一个重载的方法签名的一部分,所以下面的方法定义可能不起作用。(译注:不能同时出现在一个类中,他们被视为同样的签名,同样签名的方法在一个类中只能出现一次)
public int M(int[] value);
public ref int M(int[] value);
但是下面的定义将会起作用,因为参数在第一个方法中有ref,在第二个方法中没有ref,是传值。(译注:参数是方法签名的一部分)
public ref int M(ref int[] value);
public ref int M(int[] value);
因此,为了重载一个ref返回结果方法,你需要满足如下参数规则:
- 使用不同数量的参数,或者
- 使用不同类型的参数,或者
- 使用不同的传值方式的参数(值或者引用)。
限制
ref return有一些限制:
- 一个方法ref返回的值必须是一个ref local(译注:ref局部变量);这个ref局部变量的来源可以是这个方法的一个实际的ref/out参数,或者是声明方法所在类型中的一个字段。
- 不能引用返回空类型。
- 不能直接引用返回null。但是你可以返回一个ref局部变量,它的值是null。
- 不能从异步方法ref返回,因为在异步方法返回时,返回值可能是不确定的。
- 不允许ref返回常量和枚举。
结论
ref返回结果是C#语言的扩展,它可以通过减少从方法返回中复制值的可能性来提高代码的性能。这对于那些低级别的编程组件很有用,比如互操作性、跨平台或资源受限的场景(移动、物联网等)。要使用这个特性,你需要使用C# language 7.0,升级你的Visual Studio版本到2017年或更高版本。
系列文章:
- [译]C# 7系列,Part 1: Value Tuples 值元组
- [译]C# 7系列,Part 2: Async Main 异步Main方法
- [译]C# 7系列,Part 3: Default Literals 默认文本表达式
- [译]C# 7系列,Part 4: Discards 弃元
- [译]C# 7系列,Part 5: private protected 访问修饰符
- [译]C# 7系列,Part 6: Read-only structs 只读结构
- [译]C# 7系列,Part 7: ref Returns ref返回结果 (本文)
- [译]C# 7系列,Part 8: in Parameters in参数
- [译]C# 7系列,Part 9: ref structs ref结构
- [译]C# 7系列,Part 10: Span<T> and universal memory management Span<T>和统一内存管理 (完)
[译]C# 7系列,Part 7: ref Returns ref返回结果的更多相关文章
- [译]C# 7系列,Part 9: ref structs ref结构
原文:https://blogs.msdn.microsoft.com/mazhou/2018/03/02/c-7-series-part-9-ref-structs/ 背景 在之前的文章中,我解释了 ...
- [译]C# 7系列,Part 8: in Parameters in参数
原文:https://blogs.msdn.microsoft.com/mazhou/2018/01/08/c-7-series-part-8-in-parameters/ 背景 默认情况下,方法参数 ...
- [译]C# 7系列,Part 1: Value Tuples 值元组
Mark Zhou写了很不错的一系列介绍C# 7的文章,虽然是2年多年前发布的,不过对于不熟悉C# 7特性的同学来说,仍然有很高的阅读价值. 原文:https://blogs.msdn.microso ...
- [译]C# 7系列,Part 2: Async Main 异步Main方法
原文:https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main/ 你大概知道,C#语言可以构建两种 ...
- [译]C# 7系列,Part 3: Default Literals 默认文本表达式
原文:https://blogs.msdn.microsoft.com/mazhou/2017/06/06/c-7-series-part-3-default-literals/ C#的default ...
- [译]C# 7系列,Part 4: Discards 弃元
原文:https://blogs.msdn.microsoft.com/mazhou/2017/06/27/c-7-series-part-4-discards/ 有时我们想要忽略一个方法返回的值,特 ...
- [译]C# 7系列,Part 5: private protected 访问修饰符
原文:https://blogs.msdn.microsoft.com/mazhou/2017/10/05/c-7-series-part-5-private-protected/ C#有几个可访问性 ...
- [译]C# 7系列,Part 6: Read-only structs 只读结构
原文:https://blogs.msdn.microsoft.com/mazhou/2017/11/21/c-7-series-part-6-read-only-structs/ 背景 在.NET世 ...
- [译]C# 7系列,Part 10: Span<T> and universal memory management Span<T>和统一内存管理
原文:https://blogs.msdn.microsoft.com/mazhou/2018/03/25/c-7-series-part-10-spant-and-universal-memory- ...
随机推荐
- 【集训Day2】字符串
字符串(string) [问题描述] 给一个字符串T,问在字符串T 中可以包含最多多少个不重叠的字符串S. 字符串中的每个字符为小写或者大写字母. [输入格式] 第一行输入一个字符串S. 第二行输入一 ...
- Java包package之间调用问题-cmd运行窗口编译运行
问题:在使用了java包机制(package)后,编译出现错误:找不到或无法加载主类 xxx 的错误提示信息(各种编译不通过) 先给演示结果: 编译:javac -d classes src/a/He ...
- 【前端】 在前端利用数学函数知识+box-shadow解波浪图形
序 今天正在刷数学函数相关题目,刷到了下面这篇文章,哇哦-有意思. 利用cos和sin实现复杂的曲线.传送门在下面. CSS 技巧一则 -- 在 CSS 中使用三角函数绘制曲线图形及展示动画 正巧在复 ...
- C#笔记01——注释、进制、基本数据类型、量和输入输出函数
一.注释 1.单行注释 使用方法:行首加 ” //“: VS2019中的快捷键(以后如果不特加说明都是VS2019): 注释(CTRL+E,C): 取消注释(CTRL+E,U): 2.多行注释 使用方 ...
- Java中的Calendar 类和SimpleDateFormat 类
1.Calendar 类:import java.util.Calendar; Calendar cal = Calendar.getInstance(); ...
- 多进程使用同一log4j配置导致的日志丢失与覆盖问题
最近接手了一个流传很多手的魔性古早代码,追日志时发现有明显缺失.对log4j不熟,不过可以猜测日志出问题肯定和多进程使用同一个log4j配置有关.经多次排查,终于捋清了其中逻辑.本文对排查过程进行复盘 ...
- SpringBoot第一次案例
一.Spring Boot 入门 1.Spring Boot 简介 简化Spring应用开发的一个框架: 整个Spring技术栈的一个大整合: J2EE开发的一站式解决方案: 2.微服务 2014,m ...
- 对 /langversion 无效;必须是 ISO-1、ISO-2、3、4、5 或 Default
反编译或者.net用更高版本打开时会出现这个问题,解决办法如下: 1.网页版程序,将解决方案中的Web.config中的 /langversion 的值改为指定的值,既可以解决,我这里采用的是默认值, ...
- div水平垂直居中的六种方法
在平时,我们经常会碰到让一个div框针对某个模块上下左右都居中(水平垂直居中),其实针对这种情况,我们有多种方法实现. 方法一: 绝对定位方法:不确定当前div的宽度和高度,采用 transform: ...
- Feign超时设置
转-原文:https://xli1224.github.io/2017/09/22/configure-feign/ 在分析 Feign 源码的时候,我们看到 Feign 构建代理对象是分了几层的,一 ...