未用ref或out修饰符声明的参数为值参数。
使用值参数,通过将实参的值复制到形参的方式,把数据传递到方法。方法被调用时,系统做如下操作。
值参数的实参不一定是变量。它可以是任何能计算成相应数据类型的表达式。
看一个例子:
class="brush:csharp;gutter:true;">float func1(float val) //声明方法 { float j=2.6F; float k=5.1F; .... }
下面来调用方法
float fValue1=func1(k); //实参是float类型的变量 float fValue2=func1((k+j)/3); //实参可以计算成float表达式
在把变量作用于实参之前,变量必须赋值(除非是out参数)。对于引用类型,变量可以被设置为一个实际的引用或null。
下面的代码展示了一个名为MyMethod的方法,它有两个参数,一个是MyClass型变量和一个int。
1 class MyClass 2 { 3 public int Val = 20; 4 } 5 class Program 6 { 7 8 static void MyMethod(MyClass f1, int f2) 9 { 10 f1.Val = f1.Val + 5; 11 f2 = f2 + 5; 12 Console.WriteLine("f1.Val: {0}, f2: {1}", f1.Val, f2); 13 } 14 static void Main(string[] args) 15 { 16 MyClass a1 = new MyClass(); 17 int a2 = 10; 18 19 MyMethod(a1, a2); 20 21 Console.WriteLine("f1.Val: {0}, f2: {1}", a1.Val, a2); 22 } 23 }
我们用图来表示实参和形参在方法执行的不同阶段的值。
使用引用参数时,必须在方法的申明和调用中都使用关键字ref修饰符。
实参必须是变量,在用作实参前必须被赋值。如果是引用类型的变量,可以赋值为一个引用或者null值。
下面的代码阐明了引用参数的声明和调用的语法:
void MyMethod(ref int val) //方法声明包含ref修饰符 { //your code }
int y = 1; MyMethod(ref y); //方法调用 MyMethod(ref 3+5); //错误,形参必须是变量
在第一小节的内容中我们知道,对于值参数,系统在栈上为形参分配内存,相反对于引用参数:
由于形参名和实参名的行为,就好象指向相同的内存位置,所以在方法的执行过程中,对形参作的任何改变,在方法完成后依然有效(表现在实参变量上)。
在方法的声明和调用上都使用关键字ref.
下面的代码再次展示了方法MyMethod,但这一次参数是引用参数而不是值参数。
1 class MyClass 2 { 3 public int Val = 20; 4 } 5 class Program 6 { 7 8 static void MyMethod(ref MyClass f1,ref int f2) 9 { 10 f1.Val = f1.Val + 5; 11 f2 = f2 + 5; 12 Console.WriteLine("f1.Val: {0}, f2: {1}", f1.Val, f2); 13 } 14 static void Main(string[] args) 15 { 16 MyClass a1 = new MyClass(); 17 int a2 = 10; 18 19 MyMethod(ref a1, ref a2); 20 21 Console.WriteLine("f1.Val: {0}, f2: {1}", a1.Val, a2); 22 23 } 24 }
同样,还是用图来阐明方法执行的不同阶段实参和形参的值。
对于一个引用类型对象,不管是将其作为值参数传递还是作为引用参数传递,我们都可以在方法成员内部修改它的成员。不过,我们并没有在方法内部设置形参本身。
下面我们就来看看在方法内部设置形参本身时会发生什么。
1 class MyClass 2 { 3 public int Val = 20; 4 } 5 class Program 6 { 7 8 static void RefAsParameter(MyClass f1) 9 { 10 f1.Val = 50; 11 Console.WriteLine("After member assignment: {0}", f1.Val); 12 f1 = new MyClass(); 13 Console.WriteLine("After new object creation: {0}", f1.Val); 14 } 15 static void Main(string[] args) 16 { 17 18 MyClass a1 = new MyClass(); 19 Console.WriteLine("Before method call: {0}", a1.Val); 20 RefAsParameter(a1); 21 Console.WriteLine("After method call: {0}", a1.Val); 22 } 23 }
这段代码的输出如下:
Before method call: 20 After member assignment: 50 After new object creation: 20 After method call: 50
同样,还是用图来阐明以下几点。
除了在方法声明和方法调用时使用ref关键字之外,与上面的代码完全一样。
1 class MyClass 2 { 3 public int Val = 20; 4 } 5 class Program 6 { 7 8 static void RefAsParameter(ref MyClass f1) 9 { 10 f1.Val = 50; 11 Console.WriteLine("After member assignment: {0}", f1.Val); 12 f1 = new MyClass(); 13 Console.WriteLine("After new object creation: {0}", f1.Val); 14 } 15 static void Main(string[] args) 16 { 17 18 MyClass a1 = new MyClass(); 19 Console.WriteLine("Before method call: {0}", a1.Val); 20 RefAsParameter(ref a1); 21 Console.WriteLine("After method call: {0}", a1.Val); 22 } 23 }
这段代码的输出如下:
Before method call: 20 After member assignment: 50 After new object creation: 20 After method call: 20
我们开始说过,引用参数的行为就是将实参作为形参的别名。
这些都是老生常谈的问题,为什么还要写?
一是因为今天看书看到了与此相关的内容,回去翻了翻书,然后记录下来
二是供自己以后查阅,毕竟看博客比翻书来的快。
最后,祝大家周末愉快,玩的开心。