C# 中的参数传递
一. 基本型别 (primitive types)
对于基本型别 , 情况看起来要单纯一些 . 假设我们有如下的一段程序 :
//Example 1
using System;
class MainClass{
public static void Main(){
char c = 'a ';
Console.WriteLine("1#: c= " + c);
prmtvFun(c);
Console.WriteLine("2#: c = " + c);
}
static void prmtvFun(char c){
c++;
}
}
用 CSC 编译它并运行 , 可以发现控制台的输出为 :
1#: c= a
2#: c= a
这表明方法 prmtvFun(char c) 并没有真正地修改到参数 c 对应的 char 变量 . 这是什么原因呢 ? 在 MSDN 中 , 我们看到这样的描述 :
If a parameter is declared for a method without ref or out , the parameter can have a value associated with it . That value can be changed in the method, but the changed value will not be retained when control passes back to the calling procedure.
如果要实现修改的话 , 也是相当简单 , 只需要使用 C# 的关键字 ref 或 out. 关于 ref 关键字的介绍 , 你可以参考 MSDN( 我使用的是 MSDN Library – January 2002):
ms-help://MS.MSDNQTR.2002JAN.1033/csref/html/vclrfRef.htm.
而 out 关键字 , 则可以参考 :
ms-help://MS.MSDNQTR.2002JAN.1033/csref/html/vclrfout.htm
正如在文档中看到的那样 , 这两个关键字并没有太大的区别 . 它们的主要区别是 : 若变量作为 ref 方式传入函数 , 它必须在传入函数之前被初始化 ; 若变量作为 out 方式传入函数 , 在函数体中必须对其赋值 .
下面为一示例 :
//Example 2
using System;
class MainClass{
public static void Main(){
char c = 'm';// 必须初始化先
Console.WriteLine("1#: c = " + c);
prmtvFunWithRef(ref c); // 调用时 , 必须显式使用 ref 关键字
Console.WriteLine("2#: c = " + c);
char d; // 不必初始化 , 当然也可以初始化
//Console.WriteLine("3#: d = " + d);
prmtvFunWithOut(out d); // 调用时 , 必须显式使用 out 关键字
Console.WriteLine("4#: d = " + d);
}
static void prmtvFun(char c){
c = 's';//the changed value will not be retained when control passes back to the calling procedure
}
static void prmtvFunWithRef( ref char c ){// 使用 ref 关键字 ,c 必须被初始化过
c = 's';
}
static void prmtvFunWithOut( out char c ){// 使用 out 关键字
c = 'n';// 必须在函数体内 , 对 c 进行赋值操作
}
}
这是它的输出 :
1#: c = m
2#: c = s
4#: d = n
毫无疑问 , 这次的修改是成功的 .
结论 :
对于基本型别 (primitive types), 在作为函数的参数传递的时候 , 如果没有使用 ref 或 out 关键字 , 那么在函数体内部 , 对其进行的任何修改都不会波及其本身 . 如果正确使用了 ref 或 out 关键字 , 那么对于参数的操作 , 其对应的 " 本尊 " 也会感同身受 .
二. 对象类型 (object types)
对于对象类型 , 如果你认为由于其结构的相对复杂性 , 作为参数传递时将面临比基本型别更为复杂的情形 , 那你就错了 . 事实上 , 它的传递并不复杂 :
//Example 3
using System;
class TestClass{//a class for test
private char c;
public char C{// 简单的属性
set{
c = value;
}
get{
return c;
}
}
// 构造函数
public TestClass(char c){
this.c = c;
}
}
//============ TestClass 定义到此结束 ===========
class MainClass{//Main Class
public static void Main(){
TestClass tc = new TestClass('a');
Console.WriteLine("1#: " + tc.C);
objFun(tc,'b');
Console.WriteLine("2#: " + tc.C);
objFunWithRef(ref tc,'c');
Console.WriteLine("3#: " + tc.C);
objFunWithOut(out tc,'d');// 这里简单地使用 tc, 这与没有 new 过的对象效果是一样的
Console.WriteLine("4#: " + tc.C);
}
static void objFun(TestClass tc,char newC){
tc.C = newC;
}
static void objFunWithRef(ref TestClass tc,char newC){
tc.C = newC;
}
static void objFunWithOut(out TestClass tc,char c){
tc = new TestClass(c);
}
}
在上面的程序中 , 首先定义了一个 TestClass 类 . 这个类相当简单 , 向外只提供了一个构造函数和 char 型 C 属性 . 在 MainClass 类中 , 我们定义了一些同基本型别相似的测试函数来判断对象类型的传递方式 . 整个程序的输出为 :
1#: a
2#: b
3#: c
4#: d
<SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: