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

static void Invoke1(MyClass myClass)
{
myClass.Id = ;
} static void Invoke2(MyClass myClass)
{
myClass = new MyClass { Id = };
} var myClass = new MyClass { Id = };//原始值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 = ;
}
static void Invoke2(ref MyClass myClass)
{
myClass = new MyClass { Id = };
} var myClass = new MyClass { Id = };//原始值100
Invoke1(myClass);
Console.WriteLine(myClass.Id); //100变为0
Invoke2(ref myClass);
Console.WriteLine(myClass.Id); //0变为50
 
这里的现象是:
  • 引用类型的参数,函数中的改变不一定会影响原来的参数
  • 即使是引用类型,加上ref关键字以后也可能产生不一样的结果
那么ref关键字 和 传址调用还不是一回事,那怎么理解?
 
正常情况下(没有ref等关键字)的传参是怎么传的(包括引用类型和值类型)?
答案:传栈的副本
 
不管是值类型还是引用类型:

传过去的都是栈的副本:新的栈地址(栈的地址有改变) + 值副本(完全不变)

那么引用类型和值类型的参数传参行为是有区别的,区别在这里:

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

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

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

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

myClass = new MyClass { Id = 50 };

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

正常传参过程中值类型和引用类型内存示意图:

那么ref关键字到底是有什么作用?

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

ref以及传值传址的理解的更多相关文章

  1. [SAP ABAP开发技术总结]Form(subroutine)、Function参数传值传址

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  2. intent传值传对象跳转

    intent传值传对象跳转 1.传值 //原activity中存入一个字段 intent = new Intent(From.this, To.class); intent.putExtra(&quo ...

  3. 关于c#中”ref”和”out”关键字的一些理解

    一. 综述(本文内容大部分来自网络,经本人整理而成,仅供学习参考,不免理解错误,欢迎批评指正) 在c#中,方法的参数传递有四种类型: (1) 传值参数(by value) 传值参数无需额外的修饰符.传 ...

  4. 使用泛型元组替代out与ref形式传参

    我们在开发的时候,都会常常遇到一个问题,如何为逻辑上返回多个对象设计方法签名.通常我们会想到使用out或ref修饰参数,或者麻烦些自己设定一个类型内部包含多个Object类型属性来接收多个返回值.这两 ...

  5. C#传真传址 结构体

    1.传真  传址 namespace 传值_传址 { class Program { //格式1:无参无返 public void LeiJia() { Console.Write("请输入 ...

  6. 浅谈对java中传参问题的理解

    之前用的c/c++比较多,在c/c++中对于传参类型,无外乎就是传值.传引用.传指针这几种.但在java中,由于没有指针类型,其传参的方式也发生了相应的变化.在网上找了找,按我之前的理解,java中传 ...

  7. react组件传值传方法

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. 简谈Java传值传引用

    本随笔旨在强化理解传值与传引用   如下代码的运行结果 其中i没有改变,s也没有改变. 但model中的值均改变了. i :100s :hellomodel :testchangemodel2 :ch ...

  9. HTTP协议GET方法传参最大长度理解误区

    结论 HTTP 协议未规定GET和POST的长度 GET的最大长度是因为浏览器和WEB服务器显示了URI的长度 不同浏览器和WEB服务器,限制的最大长度不同 若要支持IE,则最大长度为2083 byt ...

随机推荐

  1. 版本管理工具(git)

    Git是一个开源的分布式版本控制系统 工作区: 电脑目录中,git_test文件夹就是一个工作区. 版本库: 在进行git操作的时候,会生成一个隐藏目录.git,这是git的版本库,其中stage(或 ...

  2. day62 django入门(3)

    目录 一.无名有名分组的反向解析 1 无名分组的反向解析 2 有名分组的反向解析 二.路由分发 三.名称空间(了解) 四.伪静态(了解) 五.虚拟环境(了解) 六.django版本区别 1 url的区 ...

  3. java 面向对象(十五):面向对象的特征三:多态性

    1.多态性的理解:可以理解为一个事物的多种形态.2.何为多态性:对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)举例:Person p = new Man();Object obj ...

  4. OSI物理层之数据通信基础知识

    @ 目录 物理层的基本概念 物理层的主要任务 数据通信的基础知识 典型的数据通信模型 相关术语 信道的基本概念 基带(base band)信号和带通(band pass)信号 几种最基本的调制方法 常 ...

  5. 什么是控制反转(IoC)?什么是依赖注入(DI)?以及实现原理

    ​ IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合.更优良的程序.传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试:有了 ...

  6. Java中使用方法的注意事项

    Java方法使用的注意事项 本文列举了几个小白在java中使用方法应该注意的几个地方 1. 方法应该定义在类中2.方法中不可以再嵌套方法3.方法定义的前后顺序无所谓4.想要执行方法必须要调用5.如果方 ...

  7. 一分钟部署nacos

    第一步:下载nacos包 https://github.com/alibaba/nacos/releases  D:\testNacos\nacos-server-1.3.0\nacos\bin 最后 ...

  8. http连接池存在的问题

    连接的有效性检测是所有连接池都面临的一个通用问题,大部分HTTP服务器为了控制资源开销,并不会 永久的维护一个长连接,而是一段时间就会关闭该连接.放回连接池的连接,如果在服务器端已经关闭,客 户端是无 ...

  9. Reface.AppStarter 基本示例

    Reface.AppStarter 向应用层提供以下几项 核心 功能 以模块化组织你的应用程序 自动注册组件至 IOC 容器 自动映射配置文件至配置类 在模块定义类中额外追加组件至 IOC 容器 在模 ...

  10. Qt_IO系统_二进制读写

    目录 Qt中的读写 QDataStream QDataStream Qt 是如何存储数据的 如何保证读取数据的正确性? --> 魔术数字,文件版本,Qt版本 魔术数字 文件版本 Qt 版本 读取 ...