在 Julia 對設定物件欄位增加檢查 - setproperty 與 setfield
在 Julia 中存取物件的欄位的時候,設計者會想要對於存取的過程中加上一些檢查,這個想法是來自於傳統物件導向 getter/setter 的概念。
例如,在自己設計的物件當中有兩個矩陣,這兩個矩陣可以讓使用者隨意更新,但是需要保證這兩個矩陣的維度是一致的。這個時候就需要在使用者更新矩陣的時候加入檢查,以確保兩個矩陣之間的維度是一致的,如果檢查不通過,可能會跳例外或者是不讓矩陣被更新。
那如果要在 Julia 中,存取物件時加入檢查的程式碼,要怎麼辦呢?
Julia 提供了兩個 setter 來提供設定物件的欄位:setproperty!
跟 setfield!
setfield!
是屬於內部的 API,這個不能覆寫,所以我們要用的是 setproperty!
。
setproperty!
的 API 可以藉由查詢文件得知:
1 | setproperty!(value, name::Symbol, x) |
也就是當你在呼叫 a.b = c
其實就會去呼叫 setproperty!(a, :b, c)
。
依據上述的例子,我們可以示範以下程式碼:
1 | mutable struct Foo |
這邊有幾點需要特別注意,第一是語言中的 setproperty!
第一個參數是 Any
型別,所以第一個參數一定要指定自己的型別是什麼,不然會跟語言本身的衝突。
第二個參數吃進來的是 Symbol
型別,這個也要指定,不然會發生 ambiguous 的狀況。
第三個參數為了廣義,所以是 Any
型別。
函式裡頭要區分開不同的欄位,這邊用 if-else 處理。想要用多重分派的機制處理的也是可以,但會比較麻煩,而且用到 Val()
會有效能降低的現象。
通過檢查後,要真正設定物件欄位,這邊用 setfield!(obj, name, x)
是比較好的作法。如果呼叫 obj.A = x
,則會去呼叫 setproperty!(obj, :A, x)
,就會變成無限遞迴呼叫了,所以 =
跟 setproperty!
在這邊都沒辦法用。
最後,我們把要檢查的欄位都處理好之後,我們不想要動到其他的欄位設定的行為,那就讓其他的欄位都用 setfield!(obj, name, x)
處理掉吧!