• 【整理】引用类型与ref传递实例精解


    一直以来对于在引用类型前加ref传递变量和不加ref传递引用类型变量的区别不是很清楚,最近看到网上一个朋友给的一道题,仔细思索和编写代码测试,终于明白其中区别了。现整理如下,以供自己和朋友们理解和记忆。

    ref是使变量通过引用的方式传递,而引用类型本身也是通过引用方式传递变量,所以一直纠结在这两者的区别?经过思考、查证资料以及编写代码证实,两者的区别在于:

    引用类型前不加ref时,只能修改所引用对象的值,而不能对所引用对象的引用进行更改。

    而在引用类型前加ref时,不仅可以修改所引用对象的值,也可以改变对所引用对象的引用。

    通过下面测试代码,能够很好的反映出来。代码中使用了非安全代码(指针),通过使用指针显示对象的内存地址,可以观察出对对象引用的变化。

    不加ref传递:

    1 class Value
    2 {
    3 public int i = 15;
    4 }
    5 class Program
    6 {
    7 static unsafe void Main(string[] args)
    8 {
    9 Program t = new Program();
    10 t.first();
    11 }
    12 public unsafe void first()
    13 {
    14 int i = 5;
    15 Value v = new Value();
    16 v.i = 25;
    17
    18 ///查看V实例的地址
    19   fixed (int* pi = &(v.i))
    20 {
    21 Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
    22 }
    23
    24 second(v, i);
    25
    26 ///执行完second函数后V实例的地址
    27 fixed (int* pi = &(v.i))
    28 {
    29 Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
    30 }
    31
    32 Console.WriteLine(v.i);
    33 }
    34 public unsafe void second(Value v, int i)
    35 {
    36 i = 0;
    37 v.i = 20;
    38
    39 ///重新指定前V实例的地址
    40 fixed (int* pi = &(v.i))
    41 {
    42 Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
    43 }
    44
    45 Value val = new Value();
    46 v = val; ///创建一个v的副本,指向val,因此v的值不会被val改变
    47
    48 ///val实例的地址
    49 fixed (int* pi = &(val.i))
    50 {
    51 Console.WriteLine("val.i Address 0x{0:X}", (int)pi);
    52 }
    53
    54 ///v的副本地址
    55 fixed (int* pi = &(v.i))
    56 {
    57 Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
    58 }
    59 Console.WriteLine(v.i + " " + i + " ");//15,0 可以理解
    60 }
    61 }

    运行结果如下:

    在这里,由于不能改变对V引用对象的引用,因此在执行“V=Val”时,其实是创建了一个V对象的副本对象,然后将副本对象的引用指向Val对象。

    加ref传递:

    class Value
    {
    public int i = 15;
    }
    class Program
    {
    static unsafe void Main(string[] args)
    {
    Program t
    = new Program();
    t.first();
    }
    public unsafe void first()
    {
    int i = 5;
    Value v
    = new Value();
    v.i
    = 25;

    ///查看V实例的地址
    fixed (int* pi = &(v.i))
    {
    Console.WriteLine(
    "V.i Address 0x{0:X}", (int)pi);
    }

    second(
    ref v, i);

    ///执行完second函数后V实例的地址
    fixed (int* pi = &(v.i))
    {
    Console.WriteLine(
    "V.i Address 0x{0:X}", (int)pi);
    }

    Console.WriteLine(v.i);
    }
    public unsafe void second(ref Value v, int i)
    {
    i
    = 0;
    v.i
    = 20;

    ///重新指定前V实例的地址
    fixed (int* pi = &(v.i))
    {
    Console.WriteLine(
    "V.i Address 0x{0:X}", (int)pi);
    }

    Value val
    = new Value();
    v
    = val; ///v直接指向val,v断开了与之前连接

    ///val实例的地址
    fixed (int* pi = &(val.i))
    {
    Console.WriteLine(
    "val.i Address 0x{0:X}", (int)pi);
    }

    ///v的地址
    fixed (int* pi = &(v.i))
    {
    Console.WriteLine(
    "V.i Address 0x{0:X}", (int)pi);
    }
    Console.WriteLine(v.i
    + " " + i + " ");//15,0 可以理解
    }
    }

    运行结果如下:

    在这里的话,由于加了ref传递对象V,因此在执行“V=Val”时,V对象直接指向了Val对象,因此V对象的地址不再0x12D16A4,而是Val的地址0x12D36CC。

  • 相关阅读:
    高并发网络编程之epoll详解
    位操作实现加减乘除四则运算
    堆和栈的区别
    IT思想类智力题
    C/C++基础总结
    数据库总结
    面试网络总结
    Windows内存管理和linux内存管理
    面试操作系统总结
    数据结构与算法
  • 原文地址:https://www.cnblogs.com/zwffff/p/1736778.html
Copyright © 2020-2023  润新知