zoukankan      html  css  js  c++  java
  • [译]C# 7系列,Part 7: ref Returns ref返回结果

    原文:https://blogs.msdn.microsoft.com/mazhou/2017/12/12/c-7-series-part-7-ref-returns/

    背景

    有两种方法可以将一个值传递给一个方法:

    1. 按值传递。当一个参数被传递给一个方法时,一个参数的副本(如果它是一个值类型)或一个"参数引用"的副本(如果它是一个引用类型)被传递。当您更改方法中的参数时,更改(单个赋值或复合赋值)会反映到参数/"参数引用"的副本,而不会反映到参数或“参数引用”本身。这是.NET语言中的默认方式(Visual Basic中的ByVal)。(译注:"参数引用"其实是个指针,指向另外一个它实际代表的对象,这里指的是修改这个指针本身。)
    2. 以引用的方式传递。当一个参数被传递给一个方法时,要么参数本身(如果它是一个值类型)要么“参数引用”(如果它是一个引用类型)被直接传递。没有生成其他副本。当您更改方法中的参数时,更改(单个赋值或复合赋值)将反映到参数或“参数引用”本身。这可以通过在C#中使用ref关键字或者在Visual Basic中使用ByRef关键字来表明。

    例如,FCL(.NET Framework Class Library)中的Array. resize()方法接受类型为Array的ref参数,该ref值在方法实现中进行了修改,以指向调整大小后的数组新空间。你可以继续使用该数组变量,并访问新的内存空间:

    byte[] data = new byte[10];
    Array.Resize(ref data, 20); //译注:上一行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 < 0 || position >= array.Length)
         {
             throw new ArgumentOutOfRangeException(nameof(position));
         }
    
         return ref array[position];
    }

    上述方法的目的是在数组的特定位置获取对元素的引用。稍后你可以使用这个引用来更改该元素的值;因为它是一个ref值,所以更改将应用于数组中的原始值。(译注:就是数组中的原始值会被改变)

    要使用这个方法,需要使用ref局部变量:

    private static void Main(string[] args)
    {
        int[] data = new int[10];
        Console.WriteLine($"Before change, element at 2 is: {data[2]}");
        ref int value = ref ElementAt(ref data, 2);
        // Change the ref value.
        value = 5;
        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, 2);

    你还可以在不使用ref关键字的情况下调用此方法,这时候返回的是值。(译注:不是引用)

    int value = ElementAt(ref data, 2);

    在这种情况下,程序输出如下:

    但是,你需要在两边都指定ref,或者两边都没有ref;你不能在一边指定ref而在另一边不指定ref。

    此外,以下代码也可以运行:

    ElementAt(ref data, 2) = 5;

    你将获得与第一个示例相同的输出。位置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有一些限制:

    1. 一个方法ref返回的值必须是一个ref local(译注:ref局部变量);这个ref局部变量的来源可以是这个方法的一个实际的ref/out参数,或者是声明方法所在类型中的一个字段。
    2. 不能引用返回空类型。
    3. 不能直接引用返回null。但是你可以返回一个ref局部变量,它的值是null。
    4. 不能从异步方法ref返回,因为在异步方法返回时,返回值可能是不确定的。
    5. 不允许ref返回常量和枚举。

    结论

    ref返回结果是C#语言的扩展,它可以通过减少从方法返回中复制值的可能性来提高代码的性能。这对于那些低级别的编程组件很有用,比如互操作性、跨平台或资源受限的场景(移动、物联网等)。要使用这个特性,你需要使用C# language 7.0,升级你的Visual Studio版本到2017年或更高版本。

    系列文章:

  • 相关阅读:
    ie烦人的bug篇
    javascript之复习(框架里的方法们)
    javascript之判断专题
    js 值类型和引用类型
    作用域链
    javascript解析机制——预解析
    SSH做反向代理
    怎样在 CentOS 7.0 上安装和配置 VNC 服务器
    创建GitHub技术博客全攻略
    rsync 备份 CENTOS 系统!
  • 原文地址:https://www.cnblogs.com/wenhx/p/csharp-7-series-part-7-ref-returns.html
Copyright © 2011-2022 走看看