细分起来,在C#中参数传递应该是算有四种情况的。参数传递方式有按值传递和按引用传递两种,而C#支持的类型呢,也分为两种:值类型和引用类型。所以排列组合一下,就有四种情况了:值类型按值传递、引用类型按值传递、值类型按引用传递和引用类型按引用传递。
1. 值类型按值传递
值类型变量是直接包含其数据,而引用类型包含的是对数据的引用(即数据的地址)。因此对于值类型变量按值传递时,传递的是变量的一个副本。方法内发生的对参数的更改对原变量中存储的原数据无任何影响,只会影响到变量的副本。
我们用段示例代码来解释。
1 static void Main(string[] args) 2 { 3 var number = 14; 4 5 Console.WriteLine( 6 string.Format("Inside main method.before calling [ChangeValueByValue] method, value is {0}", 7 number.ToString())); 8 ChangeValueByValue(number); 9 Console.WriteLine(string.Format( 10 "Inside main method.after calling [ChangeValueByValue] method, value is {0}", number.ToString())); 11 Console.ReadLine(); 12 } 13 14 static void ChangeValueByValue(int number) 15 { 16 number = 23; 17 }
- number变量赋值为14.
- 执行方法ChangeValueByValue时,传递number变量的副本给方法,假设变量copynumber为该副本,则其初始值和number一样,也为14。
- 方法内修改number的值为23,其实是修改number的副本,所以copynumber的值被修改为23。而number的值保持14不变。
执行结果如下:
2. 值类型按引用传递
值类型按引用传递向方法传递的是变量的地址,因此在方法内对变量的任何改变都会影响到原数据。必须使用ref或out关键字来传递变量。下面的示例使用ref来说明。
1 static void Main(string[] args) 2 { 3 var number = 14; 4 5 Console.WriteLine(string.Format("Inside main method.before calling [ChangeValueByRef] method, value is {0}", number.ToString())); 6 ChangeValueByRef(ref number); 7 Console.WriteLine(string.Format("Inside main method.after calling [ChangeValueByRef] method, value is {0}", number.ToString())); 8 Console.ReadLine(); 9 } 10 11 static void ChangeValueByRef(ref int number) 12 { 13 number = 234; 14 }
- number变量赋值为14
- 执行方法ChangeValueByRef时,传递变量number的地址(59997)到方法内,假设为copynumberaddress存放的是number变量的地址14,其实个人觉得这里应该是number变量地址的副本传递给了方法
- 方法内修改number值为234,也即对地址59997存放的数据进行修改为234。所以number的值也会变成234
执行结果如下:
3. 引用类型按值传递
引用类型按值传递和值类型按引用传递在某种程度上具有相同的行为。引用类型按值传递是向方法内传递变量地址的副本,因此对该变量的数据的修改会直接影响到原数据(注意是对数据的修改,其他修改是不会影响到原数据的,比如你在方法内,将对象指向一个同类型的新对象,是不会影响到原数据的,因为你只是将变量地址的副本改成了新对象的地址。原数据还是指向旧的地址)。请看下面简单示例说明:
1 static void Main(string[] args) 2 { 3 int[] array = { 54, 691, 1234 }; 4 Console.WriteLine(string.Format("Inside main method.before calling [ChangeRefByValue] method, value is {0}", array[0].ToString())); 5 ChangeRefByValue(array); 6 Console.WriteLine(string.Format("Inside main method.after calling [ChangeRefByValue] method, value is {0}", array[0].ToString())); 7 8 Console.ReadLine(); 9 } 10 11 static void ChangeRefByValue(int[] array) 12 { 13 array[0] = 98; 14 array = new int[5] { -2, -34, -12, -43, -392 }; 15 Console.WriteLine(string.Format("Inside method [ChangeRefByValue],the first element is {0}", array[0])); 16 }
- 创建一个新数组array,其第0个元素值为54
- 执行方法ChangeRefByValue,传递变量array地址(2000)的副本到方法内,假设copyarrayaddress存放了array地址副本
- 在方法ChangeRefByValue内,首先将array第0个元素值改为98,因为指向同样的地址单元,因此会影响到原来数组array第0个元素的值
- 然后执行
array = new int[5] { -2, -34, -12, -43, -392 };
语句,其实是将array地址的副本copyarrayaddress指向了新数组的地址(1980),而array变量还是指向的之前的地址(2000)
执行结果如下:
4. 引用类型按引用传递
引用类型按引用传递是向方法内传递变量地址(注意这里不是地址副本),因此对变量的任何改变都会影响到原变量(不管是数据的改变,还是指向新的对象)。请看下面示例:
1 static void Main(string[] args) 2 { 3 int[] array = { 98, 691, 1234 }; 4 5 Console.WriteLine(string.Format("Inside main method.before calling [ChangeRefByRef] method, value is {0}", array[0].ToString())); 6 ChangeRefByRef(ref array); 7 Console.WriteLine(string.Format("Inside main method.after calling [ChangeRefByRef] method, value is {0}", array[0].ToString())); 8 9 Console.ReadLine(); 10 } 11 12 static void ChangeRefByRef(ref int[] array) 13 { 14 array[0] = 35; 15 array = new int[5] { -2, -34, -12, -43, -392 }; 16 Console.WriteLine(string.Format("Inside method [ChangeRefByValue],the first element is {0}", array[0])); 17 }
- 创建一个新数组array,其第0个元素值为98
- 指向方法ChangeRefByRef,传递变量array的地址(2000)到方法内
- 在方法ChangeRefByRef内,首先将array第0个元素值改为35,因为指向同样的地址单元,因此会影响到原来数组array第0个元素的值
- 然后执行
array = new int[5] { -2, -34, -12, -43, -392 };
语句,将array指向新的数组newarray,其数组的地址也变成了新数组的地址(1980)。因此在执行方法后,array数组和newarray是相等的。
执行结果如下: