C#阶段提高之---交换数值
最近在从事两年前学过的C#语言工作,对于第三门接触的编程语言(第一门当然是是C语言、第二门是汇编),好多知识随时间的流过都变得不是那么熟练,刚好趁此机会学习巩固基础,厚积薄发。在此记下笔记也是想C#入门的同学参考,怎样在.NET的道路上更上一层楼。
首先就从简单的非基础的基础开始(一定要有语言的基本功底,比如:变量、函数、运算符、类、对象等等)。
我们先来做一个例子:交换两个数字的值。看一下代码:
1: public void Swap(int a, int b)
2: {
3: Console.WriteLine("交换前的两个值分别是:a={0},b={1}",a,b);
4: int temp;
5: temp = a;
6: a=b;
7: b = temp;
8: Console.WriteLine("交换后的两个值分别是:a={0},b={1}",a,b);
9: }
这是典型的按照值类型的交换,在交换过程前加入有两个数字num1=1、num2=2,调用此函数在函数内部实现交换绝对是没有问题的,也可以实现交换的输出。但是外部的num1,num2真的实现了交换吗?我们来分析一下,如果我来调用Swap(num1,num2),两个实参传入方法,形参a、b接受参数,对应的a=1,b=2,在函数内部交换的值只是形参(实参的数值的一个复制,不会对实参产生影响),所以再出的交换对num1,num2不会产生影响。一句话,这就是按值传递!按值传递只能将数据带入构造函数,不能带出构造函数,因此num1,num2不会发生变化。
那么怎样实现真正的交换数值呢?答曰:引用传递!按引用传递时将数值的地址传递过去,同时执行一个地址,一次修改一个,另一个也会发生变化!在C#中我们通常借用ref,out实现这类情况!
例如代码:
1: static void Main(string[] args)
2: {
3: int num1 = 1;
4: int num2 = 2;
5: Console.WriteLine("a={0},b={1}",num1,num2);
6: Swap(ref num1,ref num2);
7: Console.WriteLine("a={0},b={1}", num1, num2);
8:
9: }
10: public static void Swap( ref int a,ref int b)
11: {
12: int temp;
13: temp = a;
14: a = b;
15: b = temp;
16: }
这样就会交换成功!
为什么会这样呢?ref的作用是什么呢?ref 关键字使参数按引用传递。 其效果是,当控制权传递回调用方法时,在方法中对参数的任何更改都将反映在该变量中。通俗的讲按引用传递时既可以将参数带入方法,亦可以将参数值带出方法。对同一个地址的数值修改肯定会影响变量的值,因此改变,交换成功!
注意:
请不要将“通过引用传递”概念与“引用类型”概念相混淆。这两个概念不相关;方法参数无论是值类型还是引用类型,都可通过 ref 进行修饰。 因此,通过引用传递值类型时没有值类型装箱。
那么什么是Out呢?请看这个例子:
1: class Class1
2: {
3: static void Method(out int i)
4: {
5: i = 44;
6: }
7: static void Main()
8: {
9: int value;
10: Method(out value);
11: // value=44
12: }
13: }
尽管作为 out 参数传递的变量不必在传递之前进行初始化,但被调用的方法需要在返回之前赋一个值。
尽管 ref 和 out 关键字会导致不同的运行时行为,但在编译时并不会将它们视为方法签名的一部分。 因此,如果两个方法唯一的区别是:一个接受 ref 参数,另一个接受 out 参数,则无法重载这两个方法。
传值传递引用的主要区别实质是对地址的使用,按引用传递是赋值一个副本,而按引用传递时则是对地址的传递,直接修改原始。
那么对引用类型量传递时间会涉及此类问题吗?我认为是不会的,下面我们会通过一个例子来演示这个结论。首先我先说出这个结论:引用类型的变量传递是按引用传递的,直接对地址操作!(数组类型是引用类型),下面我们来操作一个数组:
1: static void FillArray(ref int[] arr)
2: {
3: if (arr==null)
4: {
5: arr = new int[10];
6: }
7: arr[0] = 1111;
8: arr[2] = 2345;
9: }
10: static void Main(string[] args)
11: {
12: int[] arr = new int[10] { 1,2,3,4,5,6,7,8,9,0};
13: FillArray(ref arr);
14: string s = "";
15: foreach (var a in arr)
16: {
17: s+= a+"\t";
18: }
19: Console.WriteLine(s);
20: }
输出结果是已经修改过的数组:
1111 2 2345 4 5 6 7 8 9 0
下面我们直接不适用ref来传递数组:
1: //注意不适用ref关键字
2: static void FillArray( int[] arr)
3: {
4: if (arr==null)
5: {
6: arr = new int[10];
7: }
8: arr[0] = 1111;
9: arr[2] = 2345;
10: }
11: static void Main(string[] args)
12: {
13: int[] arr = new int[10] { 1,2,3,4,5,6,7,8,9,0};
14: FillArray( arr);
15: string s = "";
16: foreach (var a in arr)
17: {
18: s+= a+"\t";
19: }
20: Console.WriteLine(s);
21: }
1111 2 2345 4 5 6 7 8 9 0
(【数组是按引用传递的,所以用不用ref是一样的.. 】)这样子理解是错误的,稍后会解释这是为什么。关于值类型,引用类型,按值传递,按引用传递是怎么运行的,有什么关系 后文给出明确的解释,在此记录作为学习记录。