一、值參數 未用ref或out修飾符聲明的參數為值參數。 使用值參數,通過將實參的值複製到形參的方式,把數據傳遞到方法。方法被調用時,系統做如下操作。 在棧中為形參分配空間。 複製實參到形參。 值參數的實參不一定是變數。它可以是任何能計算成相應數據類型的表達式。 看一個例子: 下麵來調用方法 在把變 ...
一、值參數
未用ref或out修飾符聲明的參數為值參數。
使用值參數,通過將實參的值複製到形參的方式,把數據傳遞到方法。方法被調用時,系統做如下操作。
- 在棧中為形參分配空間。
- 複製實參到形參。
值參數的實參不一定是變數。它可以是任何能計算成相應數據類型的表達式。
看一個例子:
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 }
我們用圖來表示實參和形參在方法執行的不同階段的值。
- 在方法被調用前,用作實參的a2已經在棧里了。
- 在方法開始前,系統在棧中為形參分配空間,並從實參複製值。
- 因為a1是引用類型,所以引用被覆制,結果實參和形參都引用堆中的同一對象。
- 因為a2是值類型,所以值被覆制,產生了一個獨立的數據項。
- 在方法的結尾,f2和對象f1的欄位都被加上了5。
- 方法執行後,形參從棧中彈出。
- a2,值類型,它的值不受方法行為的影響。
- a1,引用類型,但它的值被方法的行為改變了。
二、引用參數
使用引用參數時,必須在方法的申明和調用中都使用關鍵字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 }
同樣,還是用圖來闡明方法執行的不同階段實參和形參的值。
- 在方法被調用前,用作實參的a1,a2已經在棧里了。
- 在方法的開始,形參名被設置為實參的別名。變數a1和f1引用相同的記憶體位置,a2和f2引用相同的記憶體位置。
- 在方法的結束位置,f2和對象f1的欄位都被加上了5。
- 方法執行之後,形參的名稱已經失效,但是值類型a2和引用類型a1所指向的對象的值都被方法內的行為改變了。
三、引用類型作為值參數和引用參數
對於一個引用類型對象,不管是將其作為值參數傳遞還是作為引用參數傳遞,我們都可以在方法成員內部修改它的成員。不過,我們並沒有在方法內部設置形參本身。
下麵我們就來看看在方法內部設置形參本身時會發生什麼。
1、將引用類型對象作為值參數傳遞
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
同樣,還是用圖來闡明以下幾點。
- 在方法開始時,實參和形參都指向堆中相同的對象。
- 在為對象的成員賦值之後,他們仍指向堆中相同的對象。
- 當方法分配新的對象並賦值給形參時,方法外部的實參仍指向原始對象,而形參指向的是新對象。
- 在方法調用之後,實參指向原始對象,形參和新對象都會消失。
2、將引用類型對象作為引用參數傳遞
除了在方法聲明和方法調用時使用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
我們開始說過,引用參數的行為就是將實參作為形參的別名。
- 在方法開始時,實參和形參都指向堆中相同的對象。
- 在為對象的成員賦值之後,他們仍指向堆中相同的對象。
- 當方法分配新的對象並賦值給形參時,形參和實參都指向新對象。
- 在方法調用之後,實參指向方法內創建的新對象
四、寫在最後
這些都是老生常談的問題,為什麼還要寫?
一是因為今天看書看到了與此相關的內容,回去翻了翻書,然後記錄下來
二是供自己以後查閱,畢竟看博客比翻書來的快。
最後,祝大家周末愉快,玩的開心。