转自【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. UIImageView旋转任意角度---实现方法

    转自:http://blog.csdn.net/trandy/article/details/6626281 -(UIImageView *) makeRotation:(UIImageView *) ...

  2. 转载 C#结构体(struct)和类(class)的区别

    转载原地址: http://dotnet.9sssd.com/csbase/art/8 C#结构体和类的区别问题:在C#编程语言中,类属于引用类型的数据类型,结构体属于值类型的数据类型,这两种数据类型 ...

  3. PsLookupProcessByProcessId分析

    本文是在讨论枚举进程的时候产生的,枚举进程有很多方法,Ring3就是ZwQuerySystemInformation(),传入SysProcessesAndThreadsInformation这个宏, ...

  4. [IoC容器Unity] :Unity预览

    1.引言 高内聚,低耦合成为一个OO架构设计的一个参考标准.高内聚是一个模块或者一个类中成员跟这个模块或者类的关系尽量高,低耦合是不同模块或者不同类之间关系尽量简单. 拿咱国家举例来说,假如你是中国人 ...

  5. Linux chmod命令修改文件与文件夹权限命令代码

    在Unix和Linux的各种操作系统下,每个文件(文件夹也被看作是文件)都按读.写.运行设定权限. 以下转自:http://www.codeceo.com/article/linux-chmod-co ...

  6. ADO.NET 快速入门(七):使用数据库事务

    数据库事务用于控制数据提交到数据库.例如,在标准的账户程序,账户的借贷必须同时完成.由于电脑偶尔发生故障(电力中断.网络中断,等等),可能有些记录被更新或者添加,但是另外一些没有.为了避免这些情况,可 ...

  7. VHD更新命令(打补丁)

    DISM 查看vhd文件信息:dism /get-imageinfo /imagefile:e:\vhd\win2008r2.vhdxdism /get-imageinfo /imagefile:e: ...

  8. hadoop数据[Hadoop] 实际应用场景之 - 阿里

    上班之余抽点时间出来写写博文,希望对新接触的朋友有帮助.明天在这里和大家一起学习一下hadoop数据 Hadoop在淘宝和支付宝的应用从09年开始,用于对海量数据的离线处置,例如对日志的分析,也涉及内 ...

  9. Python的包管理工具Pip

    接触了Ruby,发现它有个包管理工具RubyGem非常好用,而且有非常完备的文档系统http://rdoc.info 发现Python下也有相同的工具,包含easy_install和Pip.只是,我没 ...

  10. 第十七章,txt文件的写入和读取数据结合练习(C++)

    #include <iostream> #include <fstream> int main(int argc, char** argv) { std::string str ...