咱們可能見到下面的代碼ui
public static void StringBuilderNoRef(StringBuilder s) {指針
s.Append(" World");對象
s = new StringBuilder("hi");blog
}內存
public static void StringBuilderRef(ref StringBuilder s) {變量
s.Append(" World");引用
s = new StringBuilder("hi");方法
}im
當咱們先建立一個實例 StringBuilder s = new StringBuilder("Hello");static
把s做爲參數分別傳遞到兩個方法的時候,咱們會發現
輸出結果是不同的,一個是 Hello World,另外一個是 hi
緣由其實就是 在調用第一個方法的時候,傳遞了這個引用類型對象的引用的副本(不是對象自己),因此對於在調用方法外部的引用和方法中的引用來講,這兩個引用都指向堆上的同一個對象。因此在修改此對象的屬性值時,修改同時會應用於內部和外部的兩個引用上。但從新分配其引用位置時,則只是修改副本引用的引用位置,原引用(方法外部)的位置不變,原引用仍是指向原來的對象。
而若是加上Ref關鍵字,這時傳入的參數則爲些引用對象的原始引用,而不是引用的副本,這樣的話,你就不但能夠修改原始引用對象的內容,還能夠再分配此引用的引用位置(用New 來從新初始化)。
若是你只想在方法中改變引用參數的內容,沒有必要使用Ref來修飾引用參數。
若是你但願在方法中改變引用對象參數的引用(調用方法外的),如從新初始化對象,則須要使用Ref關鍵字來修飾參數。
下面咱們再結合下圖,闡述一下我理解的 實例 引用 對象 副本的區別和聯繫
如圖,new Person()是真正的對象是在堆內存裏面,上圖一共有3次new Person(),因此會在堆裏面開闢3個空間,產生3個實例對象,每一個對象之間都是相互獨立的,使用本身的空間。而new Person()就是一個實例化的過程,將類Person生產出不一樣的對象。
而等號的左邊$p1是一個引用變量,經過賦值運算符「=」把對象的首地址賦給「$p1「這個引用變量,
因此$p1是存儲對象首地址的變量,$p1放在棧內存裏邊,$p1至關於一個指針指向堆裏面的對象, 因此咱們能夠經過$p1這個引用變量來操做對象,
一般咱們也稱對象引用爲對象。
其實一個對象就是一個實例,而引用是指一個存放對應首地址的對象,經過$p1這個引用變量來操做對象。
副本則是拷貝了該引用的指針。