在講C#參數傳遞之前,我們先簡單討論下 c#中值類型和引用類型的定義以及區別,有助於我們更好的理解參數傳遞。
我們從內存的角度來簡單討論下值類型和引用類型的區別。我們都知道值類型存儲在棧上,引用類型分別在棧和托管堆上。如下圖:
我們通過例子來看下 值類型和引用類型存儲結構不同有哪些區別:
定義一個類 (引用類型)

1 public class Student 2 { 3 public int Age { get; set; } 4 5 public void Say() 6 { 7 Console.WriteLine("我的年齡是:"+Age); 8 } 9 }
定義一個結構(值類型)

1 public struct StructStudent 2 { 3 public int Age { get; set; } 4 5 public void Say() 6 { 7 Console.WriteLine("我的年齡是:" + Age); 8 } 9 }
在控制台輸出定義如下代碼:

1 static void Main(string[] args) 2 { 3 Student stu1 = new Student { Age=20}; 4 Student stu2 = new Student(); 5 stu2 = stu1; 6 stu2.Age = 30; 7 stu1.Say(); 8 9 stu2.Say(); 10 }
輸出結果:
我們將類換成結構,在看看結果如何
定義代碼:

1 static void Main(string[] args) 2 { 3 StructStudent stu1 = new StructStudent { Age = 20 }; 4 StructStudent stu2 = new StructStudent(); 5 stu2 = stu1; 6 stu2.Age = 30; 7 stu1.Say(); 8 stu2.Say(); 9 }
輸出結果:
我們會發現同樣的代碼 但是結果不一樣,其實 每個變量 都有其堆棧,不同的變量不能共用一個堆棧地址。直接上圖
由圖 我們可以看出 對於值類型 stu2的age屬性更改不會影響 stu1 對於引用類型 stu1 和stu2 在棧上的地址是不同的。stu1=stu2 。這是2個相等的變量 所以他們在托管堆上的引用地址是一樣的。修改age 會對stu1 stu2都造成影響。所以 2種情況下輸出結果會不一樣。
明白這些 我們就很容易理解參數傳遞了。
關於參數傳遞 分按值傳遞 和按 引用傳遞
按值傳遞 分 值類型參數按值快遞 和 引用類型參數按值傳遞
按引用傳遞分 值類型參數按引用傳遞 和引用類型參數按引用傳遞
在講解參數傳遞之前 我們還要明白一個概念 形參和實參。

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 string str = "da jia hao"; 7 //str 是實際參數 8 SayHello(str); 9 } 10 //strMessage是 形式參數 11 public static void SayHello(string strMessage) 12 { 13 Console.WriteLine(strMessage); 14 } 15 }
形參和實參的類型 個數 順序 必須要完全匹配。
1、值類型參數按值傳遞:

1 static void Main(string[] args) 2 { 3 int sum = 10; 4 Add(sum); 5 Console.WriteLine(sum); 6 } 7 8 public static void Add(int i) 9 { 10 i++; 11 }
輸出 結果:
我們可以看出sum的值 並沒有改變還是10. 我們通過圖表來說明為什么會這樣。我們知道值類型都是存儲在棧上。
2、引用類型參數按值傳遞:
定義一個引用類型:

1 public class Student 2 { 3 public int Age { get; set; } 4 }

1 static void Main(string[] args) 2 { 3 Student stu = new Student { Age =20}; 4 Add(stu); 5 Console.WriteLine(stu.Age); 6 } 7 8 public static void Add(Student stu) 9 { 10 stu.Age++; 11 }
輸出結果:
可以看出結果改變了 輸出是21.
為什么會是這樣呢,直接上圖:
3、值類型按引用傳遞

1 static void Main(string[] args) 2 { 3 int sum = 10; 4 Add(ref sum); 5 Console.WriteLine(sum); 6 } 7 8 public static void Add(ref int i) 9 { 10 i++; 11 }
輸出結果:
可以看出 這次sum的 值改變了 不是 10 而是11. 這就是ref 關鍵字的神奇作用。ref在參數傳遞中到底起了什么作用呢,我們可以理解為ref 僅僅是一個地址。在文章前面我們提到過, 每個變量都有其堆棧,不同的變量不能共用一個堆棧地址。所以形參和實參在棧上的地址是不一樣的。ref所起的作用我們可以簡單的理解為他會記住實參的地址,當形參的值改變后,他會把值映射回 之前的實參地址。
4、引用類型按引用傳遞

1 public class Student 2 { 3 public int Age { get; set; } 4 }

1 static void Main(string[] args) 2 { 3 Student stu = new Student { Age=20}; 4 Add(ref stu); 5 Console.WriteLine(stu.Age); 6 } 7 8 public static void Add(ref Student stu) 9 { 10 stu.Age++; 11 }
在使用ref 的時候 調用方法一定要加上ref關鍵字 ,Add(int i) 和Add(ref int i)是構成方法的重載的。
在參數傳遞中 我們都使用ref 作為示例,那么out關鍵字 和ref 有什么區別呢。ref和out本質基本一樣。唯一的區別就是一個是側重於修改,一個側重於輸出。
static void Main(string[] args) { int sum = 10; Add(ref sum); Console.WriteLine(sum); } public static void Add(ref int i) { i++; }
這段代碼中 我們使用ref 之前 先給 sum 賦了初始值,然后修改sum的值。如果我們使用out 可以先不給sum賦值。在方法內部賦值。
params 關鍵字 是數組類型的參數。可以指定在參數數目可變處采用參數的方法參數,在方法聲明中的 params 關鍵字之后不允許任何其他參數,並且在方法聲明中只允許一個 params 關鍵字。也就是說方法中如果有多個參數只允許有一個params參數,而且要放在方法的最后一個參數。
參見 MSDN示例:

1 // cs_params.cs 2 using System; 3 public class MyClass 4 { 5 6 public static void UseParams(params int[] list) 7 { 8 for (int i = 0 ; i < list.Length; i++) 9 { 10 Console.WriteLine(list[i]); 11 } 12 Console.WriteLine(); 13 } 14 15 public static void UseParams2(params object[] list) 16 { 17 for (int i = 0 ; i < list.Length; i++) 18 { 19 Console.WriteLine(list[i]); 20 } 21 Console.WriteLine(); 22 } 23 24 static void Main() 25 { 26 UseParams(1, 2, 3); 27 UseParams2(1, 'a', "test"); 28 29 // An array of objects can also be passed, as long as 30 // the array type matches the method being called. 31 int[] myarray = new int[3] {10,11,12}; 32 UseParams(myarray); 33 } 34 }
string 類型我們都知道是我們最常用的一種引用類型,那么sting類型的參數傳遞會是什么樣的呢,我們可以自己試驗一下,會發現string是很特殊的一個引用類型。后續我會繼續講解關於string類型。 不正之處 歡迎指正!