深入理解C#之 參數傳遞 ref out params


在講C#參數傳遞之前,我們先簡單討論下 c#中值類型和引用類型的定義以及區別,有助於我們更好的理解參數傳遞。

我們從內存的角度來簡單討論下值類型和引用類型的區別。我們都知道值類型存儲在棧上,引用類型分別在棧和托管堆上。如下圖:

我們通過例子來看下 值類型和引用類型存儲結構不同有哪些區別:

定義一個類 (引用類型)

View Code
1 public class Student
2     {
3         public int Age { get; set; }
4 
5         public void Say()
6         {
7             Console.WriteLine("我的年齡是:"+Age);
8         }
9     }

 

定義一個結構(值類型)

View Code
1 public struct StructStudent
2     {
3         public int Age { get; set; }
4 
5         public void Say()
6         {
7             Console.WriteLine("我的年齡是:" + Age);
8         }
9     }

 

在控制台輸出定義如下代碼:

View Code
 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         }

輸出結果:

 

我們將類換成結構,在看看結果如何

定義代碼:

View Code
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種情況下輸出結果會不一樣。

明白這些 我們就很容易理解參數傳遞了。

關於參數傳遞 分按值傳遞 和按 引用傳遞

按值傳遞 分 值類型參數按值快遞 和 引用類型參數按值傳遞

按引用傳遞分 值類型參數按引用傳遞 和引用類型參數按引用傳遞

在講解參數傳遞之前 我們還要明白一個概念 形參和實參

 

View Code
 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、值類型參數按值傳遞:

 

View Code
 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、引用類型參數按值傳遞:

 定義一個引用類型:

View Code
1 public class Student
2     {
3         public int Age { get; set; }
4     }
View Code
 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、值類型按引用傳遞

 

View Code
 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、引用類型按引用傳遞

View Code
1  public class Student
2     {
3         public int Age { get; set; }
4     }
View Code
 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示例:

View Code
 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類型。 不正之處 歡迎指正!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM