歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

C#的未來:不可變類

本文是 C# 的未來系列文章的最後一篇了,這次我們將討論第 159 號提案,它建議在編譯器中加入對不可變類的支持。雖說在 C# 中創建不可變類型一直以來都是可以做到的,並且C# 6 還將進一步簡化這一過程,但目前還沒有一種方式能夠“將類聲明為不可變”,並讓編譯器對這一聲明進行校驗。

這一提議看起來似乎並非十分重要,因為對類進行手動檢查也不是非常困難的事。但如果缺少了對於不可變性的某種聲明,就難以了解開發者的意圖。應 用程序的開發者可能會做出某些假設,例如可以在多線程環境中安全地使用某個類,卻發現在下一個版本中,類庫的開發者為其加入了某個屬性的 setter 方法,或是其它任何一種可變的、非線程安全的特性。

第 159 號提議推薦通過使用一個“immutable”關鍵字或者屬性,顯式地表明某個類型在任何情況下都不能更改。此外,一個不可變對象只能夠引用其它不可變對象。

不可變對象的構造函數也具有一些限制,尤其是它們不能夠通過“this”變量調用方法,因為這有可能將該對象在構造過程完成之前“洩漏”出去,從而破壞了對不可變性的承諾。至於這種洩漏應當引起一個錯誤還是一次警告,這一點可以再議。Sam Harwell 寫道:

我比較傾向於發出警告的做法,雖然這種做法並不常見,並且也不是推薦的編碼方式,但也很難肯定地說絕對沒有人需要編寫這樣的代碼。

不可變性與 Pure

第一眼看上去,不可變性與 Pure(純對象或方法)約束的作用似乎是相同的,但它們之間確實存在著一些重要的不同之處。

  • 一個純對象可以引用其它並未標注為 Pure 的對象,而正如之前所說,不可變類型不允許引用可變類型。
  • 純方法不允許進行任何可見的狀態更改,包括對當前對象與任何參數的更改。
  • 純方法或純屬性可以更改內部狀態。舉例來說,它可以將某個計算的結果進行緩存以便重用。這一點並不違反純方法的承諾,因為外部的觀察者不會察覺到內部的變化。而不可變對象則沒有這種能力。
  • 不可變對象中的方法只保證不會更改對象的狀態,但允許對輸入參數的狀態進行更改。

考慮到這些不同之處的存在,相信你會看到許多同時具有不可變性與 Pure 特性的對象。

泛型與不可變性

泛型類型也將支持不可變性。要實現這一點,通常需要為每個類型參數都設定一個不可變性的限制。但這一條規則還有待商榷,有人認為可以直接從類型的參數中推斷出不可變性,而無需顯式地將其列為不可變。

欺騙

在某些情況下,你必須對類型系統進行欺騙,這一點已經得到了認可。打個比方,ImmutableArray 是對一個普通數組的封裝。而如果按照這條提議的基本原則來說,這種行為是不允許出現的。為了處理這種場景,你可以在這個類的標注中進行聲明,表示你是有意 地違背了這條原則,並且已經仔細地考慮過這樣做的後果。這種做法與“unsafe”關鍵字的行為很相似,因為後者也是為了這種場合才出現的。

由於“unsafe”關鍵字的意思已經固定了,因此該提議考慮使用其它的關鍵字。目前來說,得到最多認可的關鍵字是“mutable”,不過看起來大家對此都不是十分滿意。

只讀性 —— 隱式或顯式

在一個不可變對象中,每個字段在語義上都是只讀的。某些開發者認為不必顯式地進行聲明,這可以減少代碼的冗長度。另一些人則認為,正如在靜態類中聲明靜態字段一樣,每個字段都應該顯式地標注為只讀。

內部或外部校驗

具體在何處強制不可變性,這一點需要認真考慮。某些人認為,正如代碼契約與 Pure 屬性一樣,不可變性也應當由某個外部分析器進行處理。這樣一來,開發團隊就能夠自行決定是否需要以編程方式強制不可變性。

另一部分開發者則認為,正因為如此,不可變性更不應當由外部工具進行處理。他們希望編譯器能夠進行不可變性的檢查,這樣就不會在無意中破壞了它的不可變性。

來自於微軟的 Jared Parsons 寫道:

我認為在這種場合使用分析器是錯誤的方案,在一個單一的 C# 項目中,要強制一系列規則的應用,甚至是對某種 C# 的變體進行分析,分析器都是一種優秀的選擇。因為我對編譯過程具有掌控權,因此可以隨意地選擇並使用分析器。

而如果需要在多個項目中強制某些規則,尤其是在這些項目屬於不同開發者的情況下,分析器的作用就降低了。沒有什麼機制能夠強制對某個項目引用通過某種分析器進行掃描,這種情況下唯一有效的強制措施就是雙方的攜手合作了。

這種情況與 Pure 屬性的承諾不同,後者只是表示在某個對象中的任何方法或是屬性都不會“產生任何可見的狀態更改”。

英文原文:C# Futures: Immutable Classes

Copyright © Linux教程網 All Rights Reserved