内容简介:當我們要根據使用者輸入的C# 7.2F# 4.1
當我們要根據使用者輸入的 OrderId 到資料庫搜尋 訂單資料 ,若找的到就回傳該筆訂單,若搜尋不到呢?一般而言有兩種處理方式,傳回 null 或拋出 exception,但實務上常會因為忘記處理 null 或 exception,而在 run-time 得到 NullReferenceException ,這種常見的錯誤,是否能在 compile-time 獲得解決呢?
Version
C# 7.2
F# 4.1
Null
var order = Order.GetById(10);
Console.WriteLine("{0}", order.Name));
getById() 會根據 user 輸入的 OrderId ,到資料庫搜尋 訂單資料 ,junior 常會這樣寫程式。
這樣的寫法乍看之下沒問題,但 code review 時一定會被 sernior 問:
Q:若找不到訂單資料而回傳 null 該怎麼辦?
var order = Order.GetById(10);
if (order == null)
{
Console.WriteLine("No orders found.");
}
else
{
Console.WriteLine("{0}", order.Name));
}
Junior 會乖乖的補上 null 判斷,如此才不會在 run-time 得到 NullReferenceException 。
若使用 null 代表 找不到資料 ,會有幾個問題:
- 必須額外加上
null判斷,否則在 run-time 會得到NullReferenceException - 因為
null也是Order型別,所以 compiler 對於null檢查無能為力,只能靠 developer 自己的細心,或靠 unit test 的 coverage 完整加以保護 - 每個 function 都可能傳回
null,是否每個 function 都必須判斷null?如此 code 會變得很髒
Exception
Q: null 的確不好,所以若找不到訂單資料就拋 exception 不就好了?
try
{
var order = Order.GetById(10);
Console.WriteLine("{0}", order.Name));
}
catch (OrderNotFoundException e)
{
Console.WriteLine("No orders found.");
}
Exception 雖然比 null 好,但依然有些問題:
-
getById()回傳Order型別,你該如何得知到底該處理null還是要處理 exception?靠文件還是要靠團隊共識? - 若 developer 忘記處理 exception,compiler 也無能為力,只能靠 developer 自己的細心,或靠 unit test 的 coverage 完整加以保護
- 每個 function 都可能傳回 exception,是否每個 function 都必須處理 exception?
try catch寫法其實也好不到哪裡,一樣會把 code 弄髒
Option
FP 語言對 null 與 exception 提出了另外一種解決方案,在 OCaml 與 F# 有 Option 型別,在 Haskell 有 Maybe 型別,讓我們可以在 compiler-time 就能處理,也不會把 code 弄髒。
open System
type Order = {
Id: int
Name: string
};
[<EntryPoint>]
let main argv =
let getById id = Some { Id = 10; Name = "Sam" }
let order = getById 10
match order with
| Some order -> printfn "%s" order.Name
| None -> printfn "No orders found."
0 // return an integer exit code
由於 getById() 可能傳回 訂單資料 ,也可能找不到資料,因此回傳的就不是 Order 型別,也不是 null ,而是另外一個 Order option 型別,也就是 Option<Order> 。
再搭配 Pattern Matching 對 Option 做處理,即可同時對 找到訂單資料 與 找不到訂單資料 進行處理。
getById() 回傳的型別不再是 Order 型別 ,而是 Order option 型別,這是個 Option 型別。
Q:什麼是 Option 型別呢 ?
type Option<'a> =
| Some of 'a
| None
Option 事實上就是 Union 型別,只是已經是先定義好 Some 與 None 兩個 case。
重點是 null 是 Order 型別,但 None 不是 Order 型別,而是 Option 型別。
Q : Option 型別對於寫程式有什麼幫助?
之前因為 null 屬於 Order 型別,也無法確定 getById() 是否拋出 exception 而造成困擾,但若確認 getById() 回傳的是 Option 而不是 Order 型別,則 client 就有心理準備,知道要怎麼處理 Option ,不需靠文件也不需靠團隊共識。
let order = Order.getById 10 printfn "%s" order.Name
就算 junior 寫出這樣的程式碼,不用等 senior 來 code review,compiler 已經編譯錯誤,因為 order 為 Option 型別,不是 Order 型別,因此無法直接由 order.Name 取值。
因為 order 不是 Order 型別,所以沒有 Name 可取,在 Intellisense 階段就已經提出警告。
為了要從 Option 取值,就一定得搭配 Pattern Matching。
match order with | Some order -> printfn "%s" order.Name | None -> printfn "No orders found."
透過 Pattern Match 的 Some 將 order 取出來。
若在 Pattern Matching 只寫了 Some ,忘記寫 None ,compiler 也會提出警告。
Summary
Option 型別具有以下優點
- 透過
Option型別,client 可以明確得知該 function 可能傳回資料,也可能不傳回資料,因此會有明確因應對策,而不像null與 exception 那樣 - 由於
Option型別不等於原本資料型別,因此 junior 無法直接對 Option 取值而造成NullReferenceException - 一定得用 Pattern Matching 處裡
Option型別,若忘記使用 Pattern Matching 或 case 不夠完整,compiler 會在 compile-time 就加以警告,不會有忘記處理Option的問題 - Pattern Matching 遠比
null checking與exception handling優雅,也不會把 code 弄髒
Conclusion
- 好的程式語言是在 compile-time 就幫我們找到錯誤,而不是要自己寫程式在 run-time 處理
-
null與 exception 除了常常忘記處理而造成NullReferenceException, 也很容易將 code 弄髒;但Option+ Pattern Matching 則可透過 compiler 幫我們檢查,code 也比較優雅 - JavaScript 已經有
Option型別草案,希望能儘早成為 JavaScript 標準
Reference
F# for fun and profit , The Option type
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Programming Python
Mark Lutz / O'Reilly Media / 2006-8-30 / USD 59.99
Already the industry standard for Python users, "Programming Python" from O'Reilly just got even better. This third edition has been updated to reflect current best practices and the abundance of chan......一起来看看 《Programming Python》 这本书的介绍吧!