CS2L7——ref和out
CS2L7——ref和out
学习 ref 和 out 的原因
学习 ref
和 out
的原因,它们可以解决 在函数内部传入的内容 里面变了 外面没变 的问题
首先需要知道两个概念:形参和实参
- 形式参数(形参):在函数定义中声明的参数
- 实际参数(实参):在函数调用时传递给函数的具体值或表达式
1 | static void ChangeValue(int value) |
形参在函数定义中充当占位符,不占用内存空间,表示函数在执行时将接受的值
形参只在函数内部可见,其作用域局限在函数内部,
在函数调用时,这些形参会申请内存空间,它们申请的内存空间用来存储实参传递的数值
形参在函数执行结束后申请的内存空间就会被释放,形参释放内存空间不会影响实参
实参作为函数调用的一部分,提供了函数在执行时所需的数据。
在函数调用时,实参的数据就会拷贝到形参申请的内存空间内,得到数据的形参再参与到函数体的逻辑执行里
因此,在函数内部修改传入的形参变量,是不能改变外部的实参的,因为两个变量不是一个存储空间
1 | static void ChangeValue(int value) |
输出:
1 | 1 |
可见不用 ref
和 out
并不能直接在改变函数内改变值类型的变量
原理是 值类型的变量在传入函数时,相当于参数 = 传入变量,
在栈空间里新开了房间并将数据拷贝给参数,因此只在函数里修改变量并不能影响原来传入的变量
注意!以上的情况都是出现在值类型变量的,因为值类型变量在函数传递时拷贝的是变量的值。
由于引用类型变量在函数传递时,拷贝的实际上是指向堆上对象的地址值,
因此,在函数内修改引用类型形参变量内部数据(注意,这里只说内部数据),是可以影响到外部的实参变量的。
1 | static void ChangeArrayValue(int[] arr) |
输出:
1 | 99 |
可见在这里,函数内将引用类型的变量改变了
原理是,引用类型的变量传入函数时,将栈空间内的地址拷贝到参数里,指向了堆空间内同一个数据,
因此在函数内修改了参数,同样会修改堆空间内的数据,因此函数外的变量也会改变
不过,虽然引用类型变量在函数传递时,函数内部修改引用类型内部数据会影响到外部变量
但是形参和实参本质上是两块内存空间存储相同的地址,
因此,如果形参引用的对象被修改了(例如为形参赋值一个新的实例化一个对象),这个改动是影响不到外部实参的
1 | static void ChangeArrayValue(int[] arr) |
输出:
1 | 99 |
可见,这里外面的值并没有改变,
原理是在函数内参数在堆空间新开了一个数据区,此时参数的地址就与改变了,
与传入参数无关,因此修改参数数据不再能函数外的变量。
ref 和 out 的使用
它们是函数参数的修饰符,当传入的形参在函数内部修改时,或者引用类型参数在内部重新申明时,外部的实参值会发生变化
对于 ref
和 out
修饰的参数,外部调用函数传入值时,前面也需要加上 ref
和 out
,以 ref
为例:
1 | static void ChangeValueRef(ref int value) |
输出:
1 | 3 |
可见,函数内部修改的形参的值,外部的实参也会变化
对于引用类型变量,效果也是一样的:
1 | static void ChangeArrayRef(ref int[] arr) |
输出:
1 | 10 |
可见即便是 new
(让内部形参指向新的对象),外部的实参变量依然会被改变
out
的作用和 ref
几乎是一致的:
1 | static void ChangeValueOut(out int value) |
输出:
1 | 3 |
ref
和 out
类似与将传入变量直接进入函数而不赋值给临时变量,因此在函数内修改变量对外部变量有效
ref 和 out 的区别
-
ref
传入的变量必须初始化,out
不需要 -
out
传入的变量必须在函数内部赋值,ref
不需要
总之 ref
和 out
的变量都会被赋值过
-
ref
传入的变量必须初始化 但是在内部可改可不改 out
传入的变量不用初始化 但是在内部必须修改该值或者赋值(因为out
的参数默认没有初始化)