C# out與ref的回憶

最近因為工作上有出現這個問題,因為幾乎沒有在使用out關鍵字,我只記得他與ref雷同,實務上我唯一會使用他的地方就是int.TryParse會遭遇到,經過短暫的驗證後撰寫這篇文章,回憶與梳理一下自己的觀念。

而會有這個所謂的「問題」,是因為我認為函式方法最好就是單一輸出,這樣才可以與上面流程的程式碼斷開,便於將思緒焦點專注在方法後面的回傳結果。如果需要多重輸出,舊式寫法應該走向Array、Hashtable或相關Collections類別,新式寫法應該走向ValueTuple才對。我的用意是這樣團隊在看彼此的程式碼時會比較易於理解與順暢,未料可能是說話的語氣不對而遭受到同事的誤解,這完全是始料未及之事,也是日後我自己需要檢討改善之處。

直接說結論好了

因為真的沒有甚麼好說的,我就直接把結論寫在這邊:

  1. out與ref沒啥區別,都是Call by Reference。(這點與我的原始觀念一致)
  2. 編譯級攔截:ref在呼叫事件方法前需要初始化,但是out不用。
  3. 編譯級攔截:out在事件方法裡面一定要初始化(也就是修改賦值給它),但是ref不用。
  4. 編譯級攔截:out號稱不會將任何調用方法前的值傳入,但畢竟是Call by Reference,所以其實是有傳入到事件方法裡面的(開Debug中斷就可以看到)。但基於「呼叫前不用初始化」這個規則,因此編譯器限制你不可以在方法一開始,就基於out的參數值去進行某種應用,例如累加:count+=1;。

綜觀上面的特性,out更偏向是一種請編譯器協助我寫出一個規範內正式的回傳結果語法,以免程式於執行期出錯的C#關鍵參數指令。如果非得要用out,基於上面的特性分析,我實在找不出一定要用out而不用ref的理由。而C# 7.0給了我另外一個啟發的答案:Out Variables,免事先宣告,亦即我在int.TryParse慣用的寫法,這樣的語法糖在「回傳運算結果」的角度上,的確有超越ref之處。

但對我而言這世界並不存在非得要用out的限制,因此我還是會回歸使用ValueTuple,比較適合閱讀上的邏輯序。

相關參考

C#7.0 out ref OutVariable