转自【Learning hard

  建议参考 『第十一回:参数之惑---传递的艺术(上)

一、引言

  对于一些初学者(包括工作几年的人在内)来说,有时候对于方法之间的参数传递的问题感觉比较困惑的,因为之前在面试的过程也经常遇到参数传递的基础面试题,这样的面试题主要考察的开发人员基础是否扎实,对于C#中值类型和引用类型有没有深入的一个理解——这个说的理解并不是简单的对它们简单一个定义描述,而在于它们在内存中分布。所以本文章将带领大家深入剖析下C#中参数传递的问题,并分享我自己的一个理解,只有你深入理解了才能在不运行程序的情况就可以分析出参数传递的结果的。

二、按值传递

对于C#中的参数传递,根据参数的类型可以分为四类:

  • 值类型参数的按值传递
  • 引用类型参数的按值传递
  • 值类型参数的按引用传递
  • 引用类型参数的按引用传递

然而在默认情况下,CLR方法中参数的传递都是按值传递的。为了帮助大家全面理解参数的传递,下面就这四种情况一一进行分析。

2.1 值类型参数的按值传递

对于参数又分为:形参和实参,形参指的是被调用方法中的参数,实参指的是调用方法的参数,下面结合代码帮助大家理解形参和实参的概念:

class Program
{
static void Main(string[] args)
{ int addNum = ;
// addNum 就是实参,
Add(addNum);
} // addnum就是形参,也就是被调用方法中的参数
private static void Add(int addnum)
{
addnum = addnum + ;
Console.WriteLine(addnum);
}
}

对于值类型的按值传递,传递的是该值类型实例的一个拷贝,也就是形参此时接受到的是实参的一个副本,被调用方法操作是实参的一个拷贝,所以此时并不影响原来调用方法中的参数值,为了证明这点,看看下面的代码和运行结果就明白了:

class Program
{
static void Main(string[] args)
{
// 1. 值类型按值传递情况
Console.WriteLine("按值传递的情况");
int addNum = ;
Add(addNum);
Console.WriteLine(addNum); Console.Read();
} // 1. 值类型按值传递情况
private static void Add(int addnum)
{
addnum = addnum + ;
Console.WriteLine(addnum);
}

运行结果为:

从结果中可以看出addNum调用方法之后它的值并没有改变,Add 方法的调用只是改变了addNum的副本addnum的值,所以addnum的值修改为2了。然而我们的分析到这里并没有结束,为了让大家深入理解传递传递,我们有必要知道为什么值类型参数的按值传递不会修改实参的值,相信下面这张图可以解释你所有的疑惑:

2.2 引用类型参数的按值传递

当传递的参数是引用类型的时候,传递和操作的是指向对象的引用(看到这里,有些朋友会觉得此时不是传递引用吗?怎么还是按值传递了?对于这个疑惑,此时确实是按值传递,此时传递的对象的地址,传递地址本身也是传递这个地址的值,所以此时仍然是按值传递的),此时方法的操作就会改变原来的对象。对于这点可能看文字描述会比较难理解下面结合代码和分析图来帮助大家理解下:

class Program
{
static void Main(string[] args)
{
// 2. 引用类型按值传递情况
RefClass refClass = new RefClass();
AddRef(refClass);
Console.WriteLine(refClass.addnum);
}
// 2. 引用类型按值传递情况
private static void AddRef(RefClass addnumRef)
{
addnumRef.addnum += ;
Console.WriteLine(addnumRef.addnum);
}
}
class RefClass
{
public int addnum=;
}

运行结果为:

为什么此时传递引用就会修改原来实参中的值呢?对于这点我们还是参数在内存中分布图来解释下:

2.3 .String引用类型的按值传递的特殊情况

对于String类型同样是引用类型,然而对于string类型的按值传递时,此时引用类型的按值传递却不会修改实参的值,可能很多朋友对于这点很困惑,下面具体看看下面的代码:

class Program
{
static void Main(string[] args)
{
// 3. String引用类型的按值传递的特殊情况
string str = "old string";
ChangeStr(str);
Console.WriteLine(str); } // 3. String引用类型的按值传递的特殊情况
private static void ChangeStr(string oldStr)
{
oldStr = "New string";
Console.WriteLine(oldStr);
}
}

运行结果为:

对于为什么原来的值没有被改变主要是因为string的“不变性”,所以在被调用方法中执行 oldStr="New string"代码时,此时并不会直接修改oldStr中的"old string"值为"New string",因为string类型是不变的,不可修改的,此时内存会重新分配一块内存,然后把这块内存中的值修改为 “New string”,然后把内存中地址赋值给oldStr变量,所以此时str仍然指向 "old string"字符,而oldStr却改变了指向,它最后指向了 "New string"字符串。所以运行结果才会像上面这样,下面内存分布图可以帮助你更形象地理解文字表述:

三、按引用传递

不管是值类型还是引用类型,我们都可以使用ref 或out关键字来实现参数的按引用传递,然而按引用进行传递的时候,需要注意下面两点:

方法的定义和方法调用都必须同时显式使用ref或out,否则会出现编译错误

CLR允许通过out 或ref参数来实现方法重载。如:

 #region CLR 允许out或ref参数来实现方法重载
private static void Add(string str)
{
Console.WriteLine(str);
} // 编译器会认为下面的方法是另一个方法,从而实现方法重载
private static void Add(ref string str)
{
Console.WriteLine(str);
}
#endregion

按引用传递可以解决由于值传递时改变引用副本而不影响引用本身的问题,此时传递的是引用的引用(也就是地址的地址),而不是引用的拷贝(副本)。下面就具体看看按引用传递的代码:

 class Program
{
static void Main(string[] args)
{
#region 按引用传递
Console.WriteLine("按引用传递的情况");
int num = ;
string refStr = "Old string";
ChangeByValue(ref num);
Console.WriteLine(num);
changeByRef(ref refStr);
Console.WriteLine(refStr);
#endregion Console.Read();
} #region 按引用传递
// 1. 值类型的按引用传递情况
private static void ChangeByValue(ref int numValue)
{
numValue = ;
Console.WriteLine(numValue);
} // 2. 引用类型的按引用传递情况
private static void changeByRef(ref string numRef)
{
numRef = "new string";
Console.WriteLine(numRef);
} #endregion
}

运行结果为:

从运行结果可以看出,此时引用本身的值也被改变了,通过下面一张图来帮忙大家理解下按引用传递的方式:

四、总结

到这里参数的传递所有内容就介绍完了。总之,对于按值传递,不管是值类型还是引用类型的按值传递,都是传递实参的一个拷贝,只是值类型时,此时传递的是实参实例的一个拷贝(也就是值类型值的一个拷贝),而引用类型时,此时传递的实参引用的副本。对于按引用传递,传递的都是参数地址,也就是实例的指针。

C#中参数传递【转】的更多相关文章

  1. webApi中参数传递

    webApi中参数传递 一:无参数的get方法: 前端:    function GetNoParam() { //为了统一:我们都采用$.ajax({}) 方法; $.ajax({ url: '/a ...

  2. java 对象的this使用 java方法中参数传递特性 方法的递归

    一.this关键字,使用的情形,以及如何使用. 1.使用的情形 类中的方法体中使用this  --初始化该对象 类的构造器中使用this --引用,调用该方法的对象 2.不写this,调用 只要方法或 ...

  3. Java中参数传递问题

    Java中参数传递可以分为值传递和引用传递,话不多说直接撸代码 1.传原始类型(int,String等)数据是值传递 package test_1; public class Test { publi ...

  4. 说说不知道的Golang中参数传递

    本文由云+社区发表 导言 几乎每一个C++开发人员,都被面试过有关于函数参数是值传递还是引用传递的问题,其实不止于C++,任何一个语言中,我们都需要关心函数在参数传递时的行为.在golang中存在着m ...

  5. 全面解析C#中参数传递

    一.引言 对于一些初学者(包括工作几年的人在内)来说,有时候对于方法之间的参数传递的问题感觉比较困惑的,因为之前在面试的过程也经常遇到参数传递的基础面试题,这样的面试题主要考察的开发人员基础是否扎实, ...

  6. 如何理解Java中参数传递只能传值?

    以前学习C#的时候,是完全在工作岗位上学习,一些底层较为深入的道理都不是很清楚.如今学习了Java,对于Java参数传递只能传值,不能传引用(指针)感到很困惑,在C#中不是常常说把某个引用传递到函数中 ...

  7. java中参数传递

    一.参数是基本类型 相当于C++传值调用,方法中的形参是实参的副本. 二.参数是类类型 类类型的参数在方法调用中,相当于C++中的传址调用.形参是实参引用同一个对象.所有形参修改则实参也修改了 三.总 ...

  8. python中参数传递之位置传递、关键字传递、包裹传递与解包裹

    原文地址https://blog.csdn.net/love666666shen/article/details/77131487 1.位置与关键字传递 (1)位置传递:先用形式参数定义,然后在调用时 ...

  9. vue-router中参数传递 && 编程式导航 && 坑 && beforeRouteEnter

    第一部分: vue-router参数传递 通过router-link我们可以向路由到的组件传递参数,这在我们实际使用中时非常重要的. 路由: { path:"/DetailPage" ...

随机推荐

  1. CSS开启硬件加速 hardware accelerated

    作者:孙志勇 微博 日期:2016年12月6日 一.时效性 所有信息都具有时效性.文章的价值,往往跟时间有很大关联.特别是技术类文章,请注意本文创建时间,如果本文过于久远,请读者酌情考量,莫要浪费时间 ...

  2. Realsense 人脸识别

    一.代码声明 下面的代码是博主参考了Intel realsense官方SDK和官方例程后写的一段较为简单的代码,实现了简单的多人脸实时检测及跟踪功能.官方的人脸检测例程功能较多,但代码量很大,阅读起来 ...

  3. codeforces Gym 100500H A. Potion of Immortality 简单DP

    Problem H. ICPC QuestTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100500/a ...

  4. 用C#.NET实现电子邮件客户程序

    用C#.NET实现电子邮件客户程序 周华清 戴晟辉(东华理工学院计算机与通信系 江西 抚州 344000) [摘要]通过C#这种VisualSTudio.NET中新引入的面向对象且类型安全的编程语言, ...

  5. HDU 5235 Friends (2015 Multi-University Training Contest 2 搜索+剪枝)

    题目链接:pid=5305">传送门 题意: n个人给定m个关系.每一个关系为x,y表示x,y是朋友.可是可能是online friends,也可能是offline friends. ...

  6. 探讨一个新的两个进程间的通信和编程模型 (Windows)

    本文探讨一个新的Windows上的两个UI进程间的通信和编程模型. 开门见山,下面是这个通信模型的梗概图: 这个模型的设计目标描述如下: (1)发送数据接口:RpcSend, RpcPost RpcS ...

  7. iOS开发——数据持久化&使用NSUserDefaults来进行本地数据存储

    使用NSUserDefaults来进行本地数据存储   NSUserDefaults适合存储轻量级的本地客户端数据,比如记住密码功能,要保存一个系统的用户名.密码.使用NSUserDefaults是首 ...

  8. SAP BW中的增强(转)

    根据所了解的资料,BW中的有关增强可分为五部分,如下: 1.  数据抽取增强,即在标准数据源中加入数据源中所不存在的字段,或者标准数据源不存在所需数据而需要自定义数据源,相关的事务代码有RSO2(用于 ...

  9. java后端模拟表单提交

    代码可实现文本域及非文本域的处理 请求代码: /** * 上传 * * @param urlStr * @param textMap * @param fileMap * @return */ pub ...

  10. MySQL · 引擎特性 · InnoDB COUNT(*) 优化(?)

    http://mysql.taobao.org/monthly/2016/06/10/ 在5.7版本中,InnoDB实现了新的handler的records接口函数,当你需要表上的精确记录个数时,会直 ...