zoukankan      html  css  js  c++  java
  • ref以及传值传址的理解

      ref(也包括out)关键字肯定都会用,传值调用和传址调用也是初学写代码时都已经历过的话题,与这相关的还有一些话题,比如值类型和引用类型有什么区别等,但是如果不仔细,可能有一些概念的混淆或者理解不够清晰(引用类型参数加ref关键字是多余的吗),本文试图以最简单的方式说明一下
      有一些常见的说法:对于值类型传参就是传值调用,对于引用类型就是传址调用。如果加上ref关键字那就是传址调用,引用调用时,会改变原参数值,值调用时不会原改变参数值,看上去好像是的,那看一个例子:
    public class MyClass{  public int Id { get; set; } }
    
    static void Invoke1(MyClass myClass)
    {
        myClass.Id = 0;
    }
     
    static void Invoke2(MyClass myClass)
    {
        myClass = new MyClass { Id = 50 };
    }
    
    var myClass = new MyClass { Id = 100 };//原始值100
    Invoke1(myClass);
    Console.WriteLine(myClass.Id); //100变为0
    Invoke2(myClass);
    Console.WriteLine(myClass.Id); //依然是0

    下面换一下将引用类型的参数加上ref关键字

    public class MyClass{  public int Id { get; set; } }
    
    static void Invoke1(MyClass myClass)
    {
        myClass.Id = 0;
    }
    static void Invoke2(ref MyClass myClass)
    {
        myClass = new MyClass { Id = 50 };
    }
    
    var myClass = new MyClass { Id = 100 };//原始值100
    Invoke1(myClass);
    Console.WriteLine(myClass.Id); //100变为0
    Invoke2(ref myClass); //这里加了ref
    Console.WriteLine(myClass.Id); //结果变了:0变为50
     
    这里的现象是:
    • 引用类型的参数,函数中的改变不一定会影响原来的参数
    • 即使是引用类型,加上ref关键字以后也可能产生不一样的结果
    那么ref关键字和传址调用还不是一回事,引用类型加传参ref关键字不是多余的(不考虑应用场景合理性),那怎么理解?
     
    正常情况下(没有ref等关键字)的传参是怎么传的(包括引用类型和值类型)?
    答案:传栈的副本
     
    不管是值类型还是引用类型,传过去的都是栈的副本:
    新的栈地址(栈的地址有改变) + 值副本(完全不变)

    那么引用类型和值类型的参数传参行为是有区别的,区别在于值类型和引用类型的存储方式:

    • 对于值类型:值副本就是原来的值
    • 对于引用类型:值副本就是原来的托管堆地址

    PS: 值类型栈上保存的值,引用类型栈上保存的托管堆的地址,真正的值在托管堆上

    值类型传参对原参数无影响:栈地址和栈上的值都是副本,当然没影响

    引用类型为什么有影响(不是所有情况都有影响):传过去的托管堆地址和原来的托管堆地址是同一个地址,引用类型数据在托管堆,所以操作是针对的同一个托管堆操作,托管堆值变了,原参数引用的也是这个托管堆,当然值也跟着变化。但是如果这种操作不是操作托管堆则不会影响以前的数据,比如把栈地址副本指向一个新的托管堆地址:

    myClass = new MyClass { Id = 50 };

    ,这种操作是在托管堆上重新分配地址,然后把托管堆地址赋值给新栈副本,也就是副本栈的值不是原来的托管堆地址了,而是新的托管堆地址,那么这种改变对于原来的栈地址是没有任何影响的。

    正常传参过程中值类型和引用类型内存示意图:
    那么ref关键字到底是有什么作用?
    答案:传参数栈
    PS: 不是传栈副本,而是参数栈,那么一切都好理解了,out也是一样的,只不过必须要赋值或者指向托管堆。但是这种情况又不一样 :Method(out var parameters),有兴趣可以看一下资料
    为什么string类型是传值调用?
    答案:string类型传参没任何特殊性,特殊性在于string类型的操作都是开辟新的托管堆,而不是改变原来托管堆值(string类型是比较特殊的引用类型,重写了一些方法和行为,这是另外一个话题)

     



  • 相关阅读:
    linux signal之初学篇
    Eclipse 每次打开workspace目录记录位置?
    [Clojure] A Room-Escape game, playing with telnet and pure-text commands
    孔雀翎----《Programming C# 》中国版 文章4版
    js一些编写的函数
    KVM,QEMU核心分析
    apche commons项目简介
    java基础—网络编程———建立聊天的形式
    学习算法
    css+html+js实现多级下拉和弹出菜单
  • 原文地址:https://www.cnblogs.com/diyoufa/p/13403486.html
Copyright © 2011-2022 走看看