C#基礎-ref、out

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";
}
輸出結果爲:
Alan1
Alan2
此時看不出不使用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 null
user2 is null
此時可以明細看錯不使用ref和使用ref的區別,一樣對在方法內部賦值User ==null,效果不同
 
引用關係圖爲:
引用類型Val自己
image 
 
調用Change(aVal)方法之後:
image
 
調用Change(ref aVal)方法之後:
image 
從上可知,若是須要真正的指向同一個引用,在方法內部的任何改變都會影響到傳入參數,仍是須要使用ref和out關鍵字。
 
3.從CLR來講,ref和out自己是同樣的,都致使傳遞指向實例的指針。
惟一的區別是:
ref參數須要調用者在調用方法以前初始化參數的值(強制要求,不賦值編譯不經過)
out參數不期望調用者在調用方法以前初始化參數默認值,即便調用者以前賦值,在調用方法內部也會從新賦值(即調用者的賦值會被替換,賦值無效,因此不建議調用者提早賦值)。
 
4.其它
a.容許對方法進行ref和out進行重載(即認爲使用ref/out跟不使用ref/out函數簽名不一致)
如下方法可以編譯經過。

public static void ChangeUser(User aUser)
{
    aUser = null;
} .net

public 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

相關文章
相關標籤/搜索