上次一篇“你寫的try…catch真的有必要嗎”引起了很多朋友的討論。本次我在code review又發現了一個問題,那就是有人有意無意的寫出了return null這樣的代碼,例如:
public User GetUser(Guid userId) { if ( /*for some reason*/) return null; return DB.GetByUserId(userId); }
這樣的寫法有木有問題?
在我看來沒有充分的理由不應該返回null,因為方法的使用者並不知道在何種條件下會得到null,從而導致臭名昭著的NullReferenceException異常。
如果由於其他原因導致無法得到一個User,我們應該拋出一個明確的異常供上層服務決定如何處理:
public User GetUser(string userName, string password) { if ( /*for some reason*/) return new SpecificException("can't get this user because ...."); return DB.GetByUserId(userId); }
在我讀過的開源項目中我幾乎沒有見到過return null的寫法。能讓我一時想到的兩個linq方法FirstOrDefault()和LastOrDefault(),這兩個方法通過命名的方式提醒使用者該方法會返回null。
說到FirstOrDefault()方法讓我想起了很多人容易犯的另一個錯誤:
public User GetUserById(Guid userId) { return list.FirstOrDefault(x=>x.UserId==userId); }
在確認數據庫中該userId必須對應一個User的情況下使用FirstOrDefault()方法,此種場景我會建議你果斷使用Single()方法。因為此時使用FirstOrDefault()會隱藏bug。你期望該userId必須得到一個User,如果Single()方法拋出異常則說明有bug出現,並且讓你在第一時間發現該bug。
F#為了減少null類型的使用引入了option類型,在將option用作函數的返回類型時,如果沒有對未定義的類型做處理,編譯器會報錯。
let invalidInt = None match invalidInt with | Some x -> printfn "the valid value is %A" x | None -> printfn "the value is None"
如果此處的模式匹配忘記編寫None->分支,編譯器將會報錯,從而提醒你必須處理invalidInt值為None時的邏輯。但是在C#中使用null類型,編譯器給予不了我們幫助,所以我們應該避免return null這樣的代碼,你覺得呢?
剛才搜了一下stackoverflow,發現一篇很有意思的討論 Should a retrieval method return 'null' or throw an exception when it can't produce the return value? 我覺得里面的回答比較准確。