C# 物件屬性的建構賦值與初始化 - C# constructor and object initializer


寫這篇是起因於在臉書討論C# DTO時我講了"建構賦值比物件初始化優先"這句話(其實我的意思是指先執行),不明白的人請我解釋是甚麼意思,所以就完整交代一下個人設計DTO物件的心得。

當然,若設計其他非DTO物件會更複雜一些...

DTO不宜設計成一個複雜的邏輯物件,一般來說我們設計DTO物件時主要是做關於[屬性]的驗證和存取層級的規格設定。

另外,屬性的驗證應止於型別與範圍的安全,所以我們用屬性的get和set方法就可以達到這個需求。

設計每個DTO首先都要面臨3個課題,如下:
1.參考型別的屬性建構時是否實例化。
2.屬性型別與範圍的安全驗證。
3.屬性值的存取保護。

DTO要做到上述課題的完美解答,設計者除了要對物件導向的精神有足夠的認知與內化,還需要正確掌握C#語言的執行方式。這裡提供兩個觀念和技巧﹔

1.要清楚DTO實例化的過程(C#建構函式與初始化程式碼區塊{}的執行順序)

有幾個重點:
1.屬於參考型別的屬性(例如List<T>)若沒有先實例化就沒辦法使用其API。
2.物件初始化語法區塊中無法執行類似 "new myClass{ myList[0] = "my string", ...}" 這樣編譯器會報錯,因為參考型別的實例化必須等到物件本身完成實例化之後,所以物件初始化要賦值屬性時不能參考到其資料結構,就算程式碼已宣告其值都要等到初始化後才能使用。類似以下例子:


2.更進階的觀念:讓關鍵屬性私有化並限制只能於建構時期賦值

由於物件建構階段比物件初始化階段先完成,所以相同屬性若在建構賦值之後會又被初始化覆蓋。

為了保護建構值我們可以在屬性private set中設計驗證程式碼,然後在建構函式傳入值到setter時驗證後才設定其值。這樣建構函式本身簡潔乾淨,屬性值又不會被初始化覆蓋或後續從外部修改。類似以下例子:




寫在最後

也許目前為止看似一切都妥當,但若是遇到非DTO而是較複雜的物件設計,程式常常會由不同程式人員維護,可能會在程式某處重複設定屬性myList的值,而我們本意是只能在建構時以參數設定myList。

有沒有方法保護程式碼呢? 答案是肯定的。

作法是把負責建構關鍵組態的程式設計成基底類別,存放到別的檔案(或編譯成.dll),這樣開發人員的衍生類別就無法存取基底的private setter。類似以下例子:

以上,歡迎交流任何想法或意見,謝謝~

留言

張貼留言

這個網誌中的熱門文章

揭開 C#.NET 官方原始碼 System.Func、System.Action、System.Delegate 及 delegate 關鍵字背後的技術面紗

從Lambda語法來探討.NET LINQ的技術底蘊到底在哪裡?