看如下代碼:
void Test(T t);
void Test(ref T t);
當T是值類型的時候,很好判斷,第一種並不能改變方法外變量的值,需要第二種方法才可以。通過查看IL代碼,可以看到第二種方法是直接傳的原變量T的地址,這里並沒有發生裝箱行為(如果發生裝箱的話,會在堆中新建一個T變量,這也不會改變原來T變量的值,因此不成立)。
當T是引用類型的時候,第一種其實也相當於值傳遞,不過是原變量的地址給了t這個變量,最終操作的是同一個對象。而用ref的話,就沒有地址復制這一步,直接把原變量的地址傳了進去,當然結果都是一樣的。
*但如下情況需要注意的是
static void Main(string[] args) { UserInfo info = new UserInfo(); Console.WriteLine("調用方法之前哈希code:{0}", info.GetHashCode()); Reset(info); Console.WriteLine("調用方法之后哈希code:{0}", info.GetHashCode()); Console.ReadKey(); } public static void Reset(UserInfo info) { Console.WriteLine("Reset賦值之前的哈希code:{0}", info.GetHashCode()); UserInfo user = new UserInfo(); info = user; Console.WriteLine("Reset賦值之后哈希code為{0}", info.GetHashCode()); }
查看輸出:


可以看到,如果不使用ref,傳入的是
引用的一個副本,這個副本存儲的地址和傳入的變量是一致的,引用同一個對象,因此哈希值相同。
給這個副本賦新的變量地址后,哈希值發生了改變。
但這總歸是發生在復制的副本身上,原來的變量,哈希值未改變。
值得注意的是,如果info里面的參數,在Reset中info = user這個語句之后,發生了任何改變,都已經不會體現到Reset外圍。
比如info.userName = "aa";
在info = user語句之后,加入 info.userName = "cc"。
在最后Console.ReadKey之前輸出userName, 它還是"aa"。
Reset里面的info已經完全指向另外一個對象了。
如果我們在Reset方法的參數之前加上ref


那就是直接使用外圍變量的值了,任何操作都直接影響外圍傳入的變量。包括上述說的userName也會被改變。整個來說就是原來變量地址上的類被換了一個。
綜合來說,如果只是改變原有變量的內部變量值之類的操作,加不加ref都一樣,當涉及到new賦值操作的時候,除非是刻意需要這么做,不然推薦加ref。