• 引用类型的按值传递


    值类型和引用类型

    值类型/引用类型包含有:

    值类型:int、double、bool、char、decimal、struct、enum
    引用类型:string、自定义类、数组、集合、object、接口
    二者的区别:

    1、值类型和引用类型在内存上存储的地方不同:

    存储:

    值类型的值是存储在内存的栈当中。

    引用类型的值是存储在内存的堆中。

    2、在传递值类型和传递引用类型时,传递方式不同;

    1.值类型的按值传递

    按值传递时,传递过去的是该值类型实例的一个拷贝。栈数据会完整地复制到目标参数中即实参和形参中的数据相同但存放在内存的不同位置.
      
    2.引用类型的按值传递

    是把它的引用在栈上复制出来一份,然后传递给方法。这样就造成了栈上的两个引用指向了托管堆上的同一个实例。

    3.值类型的按引用传递

    按引用传递的时候是不存在拷贝这步操作的,众所周知,值类型的实例是分配在栈上的,所以在按引用传递值类型的时候,其实是把该实例在栈上的地址,传递给了方法。
      
    4.引用类型的按引用传递

    引用类型的按引用传递过程,与值类型的相似,也不存在拷贝这步操作,只是将“该实例的引用”在栈上的地址,传递给了方法。

    值类型的传递

    值类型在传递的时候,传递的是值本身。

    using System;
    
    namespace 值传递和引用传递
    {
        class Program
        {
            static void Main(string[] args)
            {
                int a = 1;
                int b = a;
                b = 2;
                Console.WriteLine("a={0}",a);
                Console.WriteLine("b={0}",b);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    运行结果:
    在这里插入图片描述

    内存图:

    在这里插入图片描述

    在执行 int b=a; 时,在栈中开辟一块空间用于存储b,并将a中的值传给b。执行完 b=2; 后,b被赋予了新值,并覆盖了原来的值。根据输出结果,值传递后,b的值与a的值无关,互不影响。

    引用类型的传递

    注意:这里引用传递的运行结果放在string类型上会有所不同,这是因为string类型具有不可变性,string类型的不可变性在下文会有介绍。

    引用类型传递的时候,传递的是对这个对象的引用(即存在堆中的地址)。

    下面以自定义类(Person)来说明:

    using System;
    
    namespace 值传递和引用传递
    {
        class Program
        {
            static void Main(string[] args)
            {
                Person p1 = new Person();
                p1.Name = "张三";
                Person p2 = p1;
                p2.Name = "李四";
                Console.WriteLine("p1.Name={0}",p1.Name);
                Console.WriteLine("p2.Name={0}", p2.Name);
            }
            public class Person
            {
                private string _name;
                public string Name
                {
                    get { return _name; }
                    set { _name = value; }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    运行结果:
    在这里插入图片描述

    内存图:
    在这里插入图片描述

    创建完成p1对象后,在堆上开辟一块存储空间,并将该存储空间的地址放在栈上,栈中这块空间的地址(标识)是p1。当p1对象传递给p2后,只是将对堆中的存储的引用(堆中的地址)复制一份传给p2,完成传递后p1和p2共同指向同一块内存,所以p1或p2中任意一个更改存储内容后,另一个也会改变。

    string类型的不可变性

    using System;
    
    namespace 值传递和引用传递
    {
        class Program
        {
            static void Main(string[] args)
            {
                string a = "张三";
                string b = a;
                b = "李四";
                Console.WriteLine("a={0}", a);
                Console.WriteLine("b={0}", b);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    运行结果:
    在这里插入图片描述

    从运行结果可以看出,string类型的传递和一般引用类型不同,这是因为string类型具有不可变性:

    字符串的不可变性指的是当你给一个字符串重新赋值之后,旧的值并没有被销毁,而是重新开辟一块空间存储新值。

    当程序结束后,GC扫描整个内存,如果发现有的空间没有被指向,则立即把它销毁。

    字符串的不可变性核心原因是因为String内部的Char [ ] value属性是final修饰的,是不可变的。

    内存图:
    在这里插入图片描述

    在a赋值“张三”并传递给b后,根据“引用类型的传递”,a和b共同指向“张三”(如图:堆地址为10001)这块内存。但是,在b重新被赋值为“李四”后,堆中开辟一块空间存储“李四”,旧值“张三”依然存在,新值的堆地址(如图:堆地址为10002)赋给栈中b的存储以使b指向“李四”。至此,a和b就不再指向同一块堆空间了。故运行结果与一般引用类型有所不同。

    实战:

    引用类型值传递

    在RefreshTimerControl方法中lastTimerControl= nowTimerControl; 等于lastTimerControl重新指向了托管堆上的另外一个实例,如果不使用ref的话,在RefreshTimerControl方法执行后 mUpTime还是null。因此必须使用ref

    
    //稼动率
    mUpTime = null;
    RefreshTimerControl(ref mUpTime, StringHelper.UpTime);
            
    private void RefreshTimerControl(ref TimerControl lastTimerControl, string nowTimerControlName)
    {    
    
         var nowTimerControl = SysBaseManager.Instance.TimerControlList.Where(p => p.Name == nowTimerControlName).FirstOrDefault();
          //此次不为空
         if (nowTimerControl != null && nowTimerControl.Enable)
         {
               //上一次获取的定时器信息与当前获取的定时器信息不一致
               if (lastTimerControl == null)
               {
                    //上一次为空,首先初始化
                    InitTimerControlTimer(nowTimerControl);
               }
               else
               {
                    //上一次不为空
                    if (lastTimerControl.ToJsonString() != nowTimerControl.ToJsonString())
                            ReOpenTimerControlTimer(nowTimerControl);
                }
                lastTimerControl = nowTimerControl;
         }
         else
         {
               //此次为空,上一次不为空
               if (lastTimerControl != null)
               {
                    CloseTimerControlTimer(lastTimerControl);
    
                    lastTimerControl = null;
               }
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    String类型值传递

    var message = string.Empty;
    
    ret = MesBulkDal.deleteWorkOrderList(postData.data,message);
                       
    break;
                            
      public static int deleteWorkOrderList(string data,string message)
            {
                message = "测试";
                return Code.Fail;
             }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    运行结果:
    在这里插入图片描述

    String类型不可变性赋值

    string str1 = "hello";
     str1 = "hello world";
    
    • 1
    • 2

    string str1 = "hello";执行完毕后 栈地址是0x00aff088 堆地址0x2d92424

    在这里插入图片描述

    str1 = "hello world";执行完毕后 栈地址是0x00aff088 堆地址0x2d9243c

    在这里插入图片描述

    自己对堆栈的理解

    在这里插入图片描述

  • 相关阅读:
    蓝桥杯EDA给的原理图转PCB的时候缺少封装属性怎么办
    我说ArrayList初始容量是10,面试官让我回去等通知
    本地部署_语音识别工具_Whisper
    httprunner4学习总结6 – 手动编写测试用例
    igraph load 无法读取保存的graph attr
    C++-哈希Hash
    .net mvc 无法创建虚拟目录和无法启动IIS Express Web服务器指定的url无效 解决方法
    React18入门(第二篇)——React18+Ts项目配置husky、eslint、pretttier、commitLint
    DevOps与Git之间的代码提取与上传
    android | studio的UI布局和代码调试 | UI调试 (用于找到项目源码)
  • 原文地址:https://blog.csdn.net/weixin_46879188/article/details/133993648