[C#]浅析ref、out参数
按引用传递的参数算是C#与很多其他语言相比的一大特色,想要深入理解这一概念应该说不是一件容易的事,再把值类型和引用类型给参杂进来的话就变得更加让人头晕了。
经常看到有人把按引用传递和引用类型混为一谈,让我有点不吐不快。再加上前两天碰到的一个有意思的问题,让我更加觉得应该整理整理关于ref和out的内容了。
一、什么是按引用传递
ref和out用起来还是非常简单的,就是在普通的按值传递的参数前加个ref或者out就行,方法定义和调用的时候都得加。
ref和out都是表示按引用传递,CLR也完全不区分ref还是out,所以下文就直接以ref为例来进行说明。
大家都知道,按值传递的参数在方法内部不管怎么改变,方法外的变量都不会受到影响,这从学C语言时候就听老师说过的了。
在C语言里想要写一个Swap方法该怎么做?用指针咯。
那么在C#里该怎么做?虽然也可以用指针,但是更通常也更安全的做法就是用ref咯。
说到这里,有一点需要明确,按值传递的参数到底会不会被改变。
如果传的是int参数,方法外的变量肯定是完完全全不变的咯,可是如果传的是个List呢?方法内部对这个List的所有增删改都会反映到方法外头,方法外查一下Count就能看出来了是吧。
那么传List的这个情况,也代表了所有引用类型参数的情况,方法外的变量到底变没变?
不要听信某些论调说什么“引用类型就是传引用”,不用ref的情况下引用类型参数仍然传的是“值”,所以方法外的变量仍然是不变的。
以上总结起来就是一句话:
按值传递参数的方法永远不可能改变方法外的变量,需要改变方法外的变量就必须按引用传递参数。
PS:不是通过传参的方式传入的变量当然是可以被改变的,本文不对这种情况做讨论。
二、参数传递的是什么
按值传参传的就是值咯,按引用传参传的就是引用咯,这么简单的问题还有啥可讨论的呢。
可是想一想,值类型变量和引用类型变量组合上按值传参和按引用传参,一共四种情况,某些情况下“值”和“引用”可能指的是同一个东西。
先简单地从变量说起吧,一个变量总是和内存中的一个对象相关联。
对于值类型的变量,可以认为它总是包含两个信息,一是引用,二是对象的值。前者即是指向后者的引用。
对于引用类型的变量,可以认为它也包含两个信息,一是引用,二是另一个引用。前者仍然是指向后者的引用,而后者则指向堆中的对象。
所谓的按值传递,就是传递的“二”;按引用传递,就是传递的“一”。
也就是说,在按值传递一个引用类型的时候,传递的值的内容是一个引用。
大概情况类似于这样:
按值传递时就像是这样:
可以看到,不管方法内部对“值”和“B引用”作什么修改,两个变量包含的信息是不会有任何变化的。
但是也可以看到,方法内部是可以通过“B引用”对“引用类型对象”进行修改的,这就出现了前文所说的发生在List上的现象。
而按引用传递时就像是这样:
可以看到,这个时候方法内部是可以通过“引用”和“A引用”直接修改变量的信息的,甚至可能发生这样的情况:
这个时候的方法实现可能是这样的:
void SampleMethod(ref object obj) { //..... obj = new object(); //..... }
三、从IL来看差异
接下来看一看IL是怎么对待按值或者按引用传递的参数。比如这一段C#代码:
class Class { void Method(Class @class) { } void Method(ref Class @class) { } // void Method(out Class @class) { } }
这一段代码是可以正常通过编译的,但是取消注释就不行了,原因前面也提到了,IL是不区分ref和out的。
也正是因为这一种重载的可能性,所以在调用方也必须写明ref或out,不然编译器没法区分调用的是哪一个重载版本。
Class类的IL是这样的:
温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/70391.html
- 上一篇:wpf控件之间互相绑定
- 下一篇:WPF控件互相绑定参考代码