1.默認狀況下,C#假定全部的方法參數傳遞都是傳值的。函數
以下面的方法:spa
public static void Main(string[] args) { int val = 5; //調用AddValue方法,aVal會從新拷貝一份val的值(即aVal爲val的一個實例副本),方法內部的操做並不會改變val的值。 AddValue(val); //val值仍是5,並無加1 Console.WriteLine(val); Console.ReadLine(); } public static void AddValue(int aVal) { aVal = aVal + 1; }
若是指望在調用AddValue方法之後,val加一,有兩種方案:
一、修改AddValue,將修改後的值做爲返回值,並賦值給val。
二、使用ref關鍵字、out關鍵字。傳入參數時,再也不傳入一個原來類型的拷貝,而是直接傳入原有值類型的地址(相似於引用類型) ,這樣在方法內部的任意修改都會影響到傳入的值類型。
2.引用類型的ref、out
引用類型做爲方法參數傳入時,對象的引用(或者指向該對象的指針)會傳入方法參數,在方法內部對該對象的修改,被調用者也能直接看到。
這樣是否是就不須要ref、out關鍵字了呢?
eg:
其實,引用類型做爲方法參數傳入時,理論上仍是會新建一個變量,該變量指向和原有引用類型的指向相同。
若是在調用方法內部修改了引用類型的屬性,使用ref和不使用ref沒有區別,參考代碼爲:
public static void Main(string[] args) { User user = new User(); user.Name = "Lisa"; //不使用關鍵字ref,在調用方法內部修改User對象的屬性,也會影響傳入參數User. //由於此時的User和方法參數aUser都指向同一份引用。 ChangeUser(user); Console.WriteLine(user.Name); //使用關鍵字ref,在調用方法內部修改User對象的屬性,也會影響傳入參數User //由於此時的User和方法參數aUser是同一個對象。 ChangeUser(ref user); Console.WriteLine(user.Name); Console.ReadLine(); } public static void ChangeUser(User aUser) { aUser.Name = "Alan1"; } public static void ChangeUser(ref User aUser) { aUser.Name = "Alan2"; }輸出結果爲:Alan1Alan2此時看不出不使用ref和使用ref的區別,一樣對在方法內部修改User的Name屬性,效果同樣
若是在調用方法內部修改了引用類型的指向,使用ref和不使用ref有區別,參考代碼爲:
public static void Main(string[] args) { User user = new User(); //不使用關鍵字ref,在調用方法內部修改User對象的引用,不會影響傳入參數User. //由於在棧中有兩個獨立的指針user、aUser ChangeUser(user); if (user != null) { Console.WriteLine("user1 is not null"); } else { Console.WriteLine("user1 is null"); } //使用關鍵字ref,在調用方法內部修改User對象的引用,會影響傳入參數User. //由於在棧中僅有一個指針user ChangeUser(ref user); if (user != null) { Console.WriteLine("user2 is not null"); } else { Console.WriteLine("user2 is null"); } Console.ReadLine(); } public static void ChangeUser(User aUser) { aUser = null; } public static void ChangeUser(ref User aUser) { aUser = null; }輸出結果爲:user1 is not nulluser2 is null此時可以明細看錯不使用ref和使用ref的區別,一樣對在方法內部賦值User ==null,效果不同
引用關係圖爲:
引用類型Val自己
調用Change(aVal)方法之後:
調用Change(ref aVal)方法之後:
從上可知,若是須要真正的指向同一個引用,在方法內部的任何改變都會影響到傳入參數,仍是須要使用ref和out關鍵字。
3.從CLR來講,ref和out自己是同樣的,都致使傳遞指向實例的指針。
惟一的區別是:
ref參數須要調用者在調用方法以前初始化參數的值(強制要求,不賦值編譯不經過)
out參數不期望調用者在調用方法以前初始化參數默認值,即便調用者以前賦值,在調用方法內部也會從新賦值(即調用者的賦值會被替換,賦值無效,因此不建議調用者提早賦值)。
4.其它
a.容許對方法進行ref和out進行重載(即認爲使用ref/out跟不使用ref/out函數簽名不一致)
如下方法可以編譯經過。
public static void ChangeUser(User aUser)
{
aUser = null;
} .netpublic static void ChangeUser(ref User aUser)
{
aUser = null;
} 指針編譯經過code
可是,若是兩個方法只有ref和out的區別,是不容許的。由於兩個方法簽名的元數據是徹底相同的。
如下方法編譯不過
public static void ChangeUser(ref User aUser)
{
aUser = null;
} 對象public static void ChangeUser(out User aUser)
{
aUser = null;
} blog編譯不過: get
錯誤: 「ChangeUser」不能定義僅在 ref 和 out 上有差異的重載方法 string
b.使用ref/out關鍵字時,方法定義和調用時參數類型必須一致。 it