ref以及傳值傳址的理解


  ref(也包括out)關鍵字肯定都會用,傳值調用和傳址調用也是初學寫代碼時都已經歷過的話題,與這相關的還有一些話題,比如值類型和引用類型有什么區別等,但是如果不仔細,可能有一些概念的混淆或者理解不夠清晰(引用類型參數加ref關鍵字是多余的嗎),本文試圖以最簡單的方式說明一下
  有一些常見的說法:對於值類型傳參就是傳值調用,對於引用類型就是傳址調用。如果加上ref關鍵字那就是傳址調用,引用調用時,會改變原參數值,值調用時不會原改變參數值,看上去好像是的,那看一個例子:
public class MyClass{  public int Id { get; set; } }

static void Invoke1(MyClass myClass)
{
    myClass.Id = 0;
}
 
static void Invoke2(MyClass myClass)
{
    myClass = new MyClass { Id = 50 };
}

var myClass = new MyClass { Id = 100 };//原始值100
Invoke1(myClass);
Console.WriteLine(myClass.Id); //100變為0
Invoke2(myClass);
Console.WriteLine(myClass.Id); //依然是0

下面換一下將引用類型的參數加上ref關鍵字

public class MyClass{  public int Id { get; set; } }

static void Invoke1(MyClass myClass)
{
    myClass.Id = 0;
}
static void Invoke2(ref MyClass myClass)
{
    myClass = new MyClass { Id = 50 };
}

var myClass = new MyClass { Id = 100 };//原始值100
Invoke1(myClass);
Console.WriteLine(myClass.Id); //100變為0
Invoke2(ref myClass); //這里加了ref
Console.WriteLine(myClass.Id); //結果變了:0變為50
 
這里的現象是:
  • 引用類型的參數,函數中的改變不一定會影響原來的參數
  • 即使是引用類型,加上ref關鍵字以后也可能產生不一樣的結果
那么ref關鍵字和傳址調用還不是一回事,引用類型加傳參ref關鍵字不是多余的(不考慮應用場景合理性),那怎么理解?
 
正常情況下(沒有ref等關鍵字)的傳參是怎么傳的(包括引用類型和值類型)?
答案:傳棧的副本
 
不管是值類型還是引用類型,傳過去的都是棧的副本:
新的棧地址(棧的地址有改變) + 值副本(完全不變)

那么引用類型和值類型的參數傳參行為是有區別的,區別在於值類型和引用類型的存儲方式:

  • 對於值類型:值副本就是原來的值
  • 對於引用類型:值副本就是原來的托管堆地址

PS: 值類型棧上保存的值,引用類型棧上保存的托管堆的地址,真正的值在托管堆上

值類型傳參對原參數無影響:棧地址和棧上的值都是副本,當然沒影響

引用類型為什么有影響(不是所有情況都有影響):傳過去的托管堆地址和原來的托管堆地址是同一個地址,引用類型數據在托管堆,所以操作是針對的同一個托管堆操作,托管堆值變了,原參數引用的也是這個托管堆,當然值也跟着變化。但是如果這種操作不是操作托管堆則不會影響以前的數據,比如把棧地址副本指向一個新的托管堆地址:

myClass = new MyClass { Id = 50 };

,這種操作是在托管堆上重新分配地址,然后把托管堆地址賦值給新棧副本,也就是副本棧的值不是原來的托管堆地址了,而是新的托管堆地址,那么這種改變對於原來的棧地址是沒有任何影響的。

正常傳參過程中值類型和引用類型內存示意圖:

 

 

那么 ref關鍵字到底是有什么作用?
答案:傳參數棧
PS: 不是傳棧副本,而是參數棧,那么一切都好理解了, out也是一樣的,只不過必須要賦值或者指向托管堆。但是這種情況又不一樣 :Method(out var parameters),有興趣可以看一下資料
為什么string類型是傳值調用?
答案:string類型傳參沒任何特殊性,特殊性在於string類型的操作都是開辟新的托管堆,而不是改變原來托管堆值(string類型是比較特殊的引用類型,重寫了一些方法和行為,這是另外一個話題)

 

 

 

 



 


免責聲明!

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



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