深入剖析引用参数Ref和Out
学过C/C++的人,对C#的关键字Ref和Out应该都很好理解。它们都提供了一种可以在被调用函数内修改传递的参数的值的方法。因为这一功能很类似C/C++的指针。对于没学过C/C++的,也应该可以明白这两个参数的作用。
虽然Ref和Out都提供了修改参数值的方法,但它们还是有一点点小的区别。
1、Ref在作为参数调用函数之前,变量一定要赋值,否则会得到一个常规编译错误:使用了未赋值的变量。
2、在被调用函数内,以Ref引入的参数在返回前不必为它赋值。
3、Out在作为参数调用函数之前,变量可以不被赋值。
4、在被调用函数内,以Out引入的参数在返回前一定要至少赋值一次。
其实本质上讲,Ref更适合理解为给被调用函数传递了一个与原参考同地址的变量。而Out则可以理解为在调用函数前,先给变量找个地方,让被调用函数在给定地点放一个值。
看上去很简单不是吗?确实如此,这里是一个例子:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
然而C#毕竟是与C/C++有着不同之处的。这就是在C#内,所有的变量被分为两类:值类型和引用类型。
那么我们就会有这样的问题:将Ref和Out分别应用于引用类型和值类型的变量上,会是什么样的结果呢?
对于应用于值类型数据的情况,上面的例子已经完全讨论过了,就是完全遵守上面的四句话。而对于引用类型数据,有一个很有趣的问题,就是默认情况下(不带Ref也不带Out)它是以Ref情况而调用函数,即上面的四句话仍然满足。
看这样的一个例子:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
这让人感觉就是Ref,确实如此,默认就是在以Ref为引用类型在调用函数,所以还是要注意以下问题:
引用类型数据一定要初始化。而至于引用类型自己初始化的问题,就交给该类型自己了。如上面的问题,m_member在没有赋值前,一样可以编译的,但运行一定就不对了(但有默认值),这是因为TempClass里没有构造函数。在被调用函数内,一样的使用参数,而且所有对引用参数的改变都影响到函数外。这是默认的情况。
但如果我们强行加上Ref或者Out关键字,会是什么结果呢???
1、如果是用Ref,那么结果是和什么都没用一样!即默认就是用的Ref。(让我们少打了几个字符)
2、如果是用Out,那么要遵守上面的3,4原则,即:在调用前,不必初始化引用对象,再简单一点:就是可以不用New一个对象。但在函数内,返回前一定要New一个,并且在New之前,参数对象是不能使用的。
也就是上面说到的,Out只是在调用前分配了一个地点,在调用函数中使用该地点。注意:这里“地点”一词决不是内存地址。
再思考一个问题:如果在使用Out参考时,在调用函数前,我们已经New了一个对象,再来调用函数结果会是什么呢?
你将“丢失”一部份内存(如果在C/C++里,一定是这样的)。也就是说,在调用了函数后,函数里New的一个对象会让函数外的对象丢失,而新的对象在函数内有效,在函数外也有效。幸运的是:原来的对象的内存并不会像C/C++那样完全的丢失,它将由垃圾回收器来管理了。所以我们并不担心内存的真正丢失问题(这真是一件值得庆幸的事)。
看这样的例子:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
到此为止,或许你已经觉得你已经对Ref和Out已经十分的了解了,然而你可能无法回答下面的一个问题:它的输出是什么?
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
![](/assets/blank.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
请不要猜测它的结果,这样可能会让你犯一个错误。如果你利用前面的知识来理解,认为两个对象输出的结果是同一个对象的结果,那么你这回又是错误的!
这什么呢?前面不是说的很清楚吗?上面的函数参数一个用了ref,一个没有用,默认的都是Ref,也就是说:两个参数其实是一样的!!而且函数内用了两次New,因此,对象Class1的最后一个有效实例是最后一次New的结果,也就是说:class1和class2都将引用到(指向)函数内最后一次New的实例,所以最后输出应该都是42.
然而很不幸,这回结果是:m_class1:0,m_class2:41
而且它们的内存关系也是有点复杂的。让我们先来看看函数调用前的假想内存情况:
|
|
调用函数后的结果:
|
|
这就是为什么 会有两个结果了!!!也就是说:当我们用Ref来传递引用类型数据的一个引用时(这里的Class2就是这样的情况,它是一个指向TempClass实例1的一个引用),其实是使用的实例的一个副本。即:产生了即没有用Ref也没有用Out的效果。
[Post之后的修改]
这里请一定注意这里的调用: ChangeData4(ref m_class2,m_class2);
读者自己试着这样试试:
ChangeData4(ref m_class1,m_class2);
ChangeData4(ref m_class2,m_class1);
ChangeData4(ref m_class2,m_class1);
然后再分析一下内存,可能会有很大的收获。
最后一个问题:就是上面这种情况应用于值类型数据的时候会是什么情况呢?这就交给读者自己去解决了。
好了,最后一个引用的例子值得思考一下。希望对读者有帮助。
转载于:https://www.cnblogs.com/WuCountry/archive/2006/02/27/338514.html
深入剖析引用参数Ref和Out相关推荐
- c# 方法参数 传值or传引用?(ref,out,可变参数params,可选参数,命名参数)
目录 一.方法参数的类型----值类型和引用类型 二.一些特殊的方法参数 1.引用参数---ref 2.输出参数---out 注意:ref和out的区别 3.可变参数/参数数组-----params ...
- c# 方法参数(传值,传引用,ref,out,params,可选参数,命名参数)
一.方法参数的类型----值类型和引用类型 当方法传递的参数是值类型时,变量的栈数据会完整地复制到目标参数中即实参和形参中的数据相同但存放在内存的不同位置.所以,在目标方法中对形参所做的更改不会对调用 ...
- C#方法中参数ref和out的解析
一.C#方法中参数类型 有4种参数类型,有时候很难记住它们的不同特征,下图对它们做一个总结,使之更容易比较和对照. 二.C#方法中的参数 1.值参数 使用值参数,通过复制实参的值到形参的方式把数据传递 ...
- c#中引用类型作为值参数和引用参数问题
一.分类 C#的值类型包括:结构体(数值类型,bool型,用户定义的结构体),枚举,可空类型. C#的引用类型包括:数组,用户定义的类.接口.委托,object,字符串. 二.参数传递 对于引用类型, ...
- C#方法的参数 Ref Out Params 4种类型的参数
转载:http://www.cnblogs.com/kissdodog/archive/2013/05/11/3072815.html 一.按值传递参数 值参数是通过将实参的值复制到形参,来实现按值传 ...
- C#方法的六种参数,值参数、引用参数、输出参数、参数数组、命名参数、可选参数...
方法的参数有六种,分别是值参数.引用参数.输出参数.参数数组.命名参数.可选参数. 值参数 值参数是方法的默认类型,通过复制实参的值到形参的方式把数据传递到方法,方法被调用时,系统作两步操作: 在栈中 ...
- C#_delegate - 值参数和引用参数
值参数不能加,引用参数可以. 引用参数是共享的 using System; using System.Collections.Generic; using System.Linq; using Sys ...
- 【C++ 语言】引用 ( 引用简介 | 指针常量 | 常量指针 | 常引用 | 引用参数 | 引用 指针 对比 )
文章目录 I . 引用概念 II . 引用声明 III . 引用 地址 内存 分析 IV . 常引用 V . 引用作为参数 VI . 引用 与 指针 对比 I . 引用概念 C++ 对 C 扩充 : ...
- shell 中引用参数总结
shell 中引用参数总结 1 在shell中定义变量之后引用这个变量要用 $ 符号,例如: (一下例子是定义了一个变量 a ,然后回显这个变量) 2 在向函数传递参数后,要引用参数,因 ...
- C++const类型的引用参数
C++const类型的引用参数: 具体作用,假设实参的参数类型与引用参数不匹配,但可以转换为引用类型,程序将创建一个正常类型的临时变量,使用转化后的实参值来初始化它,然后传递一个指向该临时变量的引用. ...
最新文章
- JavaScript实现完整的ComplexNumber复数类(附完整源码)
- NAB 2019见闻:CAE视频编码与QoE
- html5 支持表格吗,html5 – 在HTML 5中使用表格很好吗?
- 2018-2019-2 网络对抗技术 20165303 Exp4 恶意代码分析
- 手机上python编程工具3和3h有区别吗_Python 高级 3
- 拿来就能用!行,这本 Python 书彻底火了!
- Android零基础入门第64节:揭开RecyclerView庐山真面目
- centos7桌面没有计算机图标,centos7下创建桌面图标的方法
- c语言利用循环结构解决密码转换,C语言课件第六章循环结构.ppt
- 3dMax模型尺寸更改与模型均匀缩放
- StanfordDB class自学笔记 (11) Indexes and Transactions
- 全球及中国电动车行业品牌竞争策略与投资机会分析报告2022版
- 同网段的VLAN隔离
- 分享几个有趣的Python小项目
- NWA Quality Analyst应用案例:在食品加工中选择和实施SPC软件
- PS动感映像插件ImageMotion 1.3中文汉化版
- 【知识兔】自学Excel之4:窗口视图控制
- 利用matlab实现AM调制解调
- 2022电大国家开放大学网上形考任务-金融企业会计非免费(非答案)
- 7.3 Python 一维数据的格式化和处理
热门文章
- graphviz安装_离线安装vscode插件,你可能会用到!
- 8.1并发集合(Concurrent Collections)
- Java I/O系统之处理流类型
- Struts2之类型转换中的错误
- [渝粤教育] 山东大学 日本历史与文化 参考 资料
- [渝粤教育] 西南科技大学 管理信息系统 在线考试复习资料(2)
- Pandas系列(十六)快速进行日期处理
- Python进阶(九)常用高级函数Counter、defaultdict、nametuple、map、reduce、filter、groupby
- lodop简单入门教程
- webpack入门1