一、引言
C#中參數的傳遞方式可以分為兩類,按值傳遞和按引用傳遞。如果再根據參數的類型進行細分,大致可以分為如下四種:
- 值類型的按值傳遞
- 引用類型的按值傳遞
- 值類型的按引用傳遞
- 引用類型的按引用傳遞
string類型作為一種特殊的引用類型,部分人認為在作為參數進行傳遞的時候,它的表現與其他的引用類型是不一致的。但是透過現象看本質,我們深入分析一下,就會發現,在作為參數進行傳遞方面,string類型與其他的引用類型是沒有區別的。
二、四種傳遞方式解析
1. 值類型的按值傳遞
按值傳遞時,傳遞過去的是該值類型實例的一個拷貝。
1 public static int Add(int i, int j) 2 { 3 i = i + 1; 4 Console.WriteLine(i); 5 return i + j; 6 } 7 ...... 8 static void Main(string[] args) 9 { 10 int a = 1; 11 Add(a, 5); 12 Console.WriteLine(a); 13 }
運行結果如下:
2. 引用類型的按值傳遞
按值傳遞時,傳遞過去的是該引用類型實例的引用的一個拷貝,這樣說可能不是很清楚,而且容易引起誤解。所謂引用,就是分配在棧上的一小塊內存區域,里面存放着該引用類型實例在托管堆上的地址。引用類型在按值傳遞的時候,其實就是把它的引用在棧上復制出來一份,然后傳遞給方法。這樣就造成了棧上的兩個引用指向了托管堆上的同一個實例。所以這就可以解釋,如下兩個方法運行結果為什么會不一致。
1 class Person 2 { 3 public string Name { get; set; } 4 } 5 ...... 6 static void ChangePerson(Person person) 7 { 8 person = new Person(); 9 person.Name = "Changing Name"; 10 Console.WriteLine(person.Name); 11 } 12 13 static void ChangeName(Person person) 14 { 15 person.Name = "Changing Name"; 16 Console.WriteLine(person.Name); 17 } 18 ...... 19 // 引用類型的按值傳遞 20 Person person = new Person() { Name="Old" }; 21 ChangeName(person); 22 Console.WriteLine(person.Name); 23 24 // 引用類型的按值傳遞 25 person = new Person() { Name = "Old" }; 26 ChangePerson(person);
27 Console.WriteLine(person.Name);
運算結果如下:
3. 值類型的按引用傳遞
按引用傳遞的時候是不存在拷貝這步操作的,眾所周知,值類型的實例是分配在棧上的,所以在按引用傳遞值類型的時候,其實是把該實例在棧上的地址,傳遞給了方法。
1 public static int Add(ref int i, int j) 2 { 3 i = i + 1; 4 Console.WriteLine(i); 5 return i + j; 6 } 7 ...... 8 // 值類型的按引用傳遞 9 int a = 1; 10 Add(ref a, 5); 11 Console.WriteLine(a);
運算結果如下:
4. 引用類型的按引用傳遞
引用類型的按引用傳遞過程,與值類型的相似,也不存在拷貝這步操作,只是將“該實例的引用”在棧上的地址,傳遞給了方法。
1 class Person 2 { 3 public string Name { get; set; } 4 } 5 ...... 6 static void ChangeNameRef(ref Person person) 7 { 8 person.Name = "Changing Name"; 9 Console.WriteLine(person.Name); 10 } 11 static void ChangePerson(ref Person person) 12 { 13 person = new Person(); 14 person.Name = "Changing Name"; 15 Console.WriteLine(person.Name); 16 } 17 // 引用類型的按引用傳遞 18 person = new Person() { Name = "Old" }; 19 ChangeNameRef(ref person); 20 Console.WriteLine(person.Name); 21 22 person = new Person() { Name = "Old" }; 23 ChangePerson(ref person); 24 Console.WriteLine(person.Name);
運算結果如下:
PS:有些人認為,引用類型的按引用傳遞感覺與按值傳遞沒什么區別,給引用類型加上ref和out也沒什么意義。通過上述的運算結果對比一下就可以知道,其實這種認識是錯誤的。
三、string類型在參數傳遞方面是否異於其他引用類型?
string類型作為一種特殊的引用類型,它的特殊性具體有哪些,與我們的主題關系不大,我們只需要了解它的一個特性即可:當string類型的值發生變化時,相當於執行其他引用類型的new操作,這種說法並不准確,我們在此處並不需要深究。(需要了解string類型的特殊性,請自行百度!)正是因為這個特性,才導致在按值傳遞的時候,string類型與其他引用類型在外在表現上會有少許不同。但是根據string類型的這個特性,其實string類型的按值傳遞與上述引用類型的按值傳遞的第二個方法的執行情況從表現到本質都是一致的。
1 // string類型按值傳遞 2 static void ChangeStr(string aStr) 3 { 4 aStr = "Changing"; 5 Console.WriteLine(aStr); 6 } 7 // string類型按引用傳遞 8 static void ChangeStrRef(ref string aStr) 9 { 10 aStr = "Changing"; 11 Console.WriteLine(aStr); 12 } 13 ...... 14 // 字符串類型的按值傳遞 15 string str = "Old"; 16 ChangeStr(str); 17 Console.WriteLine(str); 18 19 // 字符串類型的按引用傳遞 20 str = "Old"; 21 ChangeStrRef(ref str); 22 Console.WriteLine(str);
運算結果如下: