2013-07-24 33 views
12

我在懷疑我對System.Collection.Generic.IReadOnlyCollection<T>語義的理解,懷疑如何使用諸如只讀和不可變等概念進行設計。讓我使用documentation描述兩個性質,我懷疑之間,其中規定不變性/只讀語義(特別是C#IReadOnlyCollection <T>)

表示一個強類型,只讀元素的集合。

取決於我是否強調的話「代表」或「只讀」(在我的腦海宣判的時候,或者大聲,如果這是你的風格),我覺得這句話的變化意味着:

  1. 當我強調'只讀'時,文檔定義了我的觀察不變性(Eric Lippert's article中使用的一個術語),這意味着只要沒有可見的突變,界面的實現可以隨心所欲公開†。
  2. 當我強調'表示'時,文檔定義了(在我看來,再次)一個不可改變的外觀(在Eric Lippert的文章中再次描述),這是一種較弱的形式,其中突變可能是可能的,但不能通過用戶。例如,IReadOnlyCollection<T>類型的屬性向用戶(即對代碼聲明類型編碼的人)表明他可能不會修改此集合。但是,聲明類型本身是否可以修改集合並不明確。
  3. 爲了完整起見:除了攜帶其成員的簽名外,接口不包含任何語義。在這種情況下,觀察或外觀不變性依賴於實現(不僅僅是實現 - 接口相關,而是實例相關)。

第一個選項實際上是我的首選解釋,雖然這個合同很容易被打破,通過從T的數組中構建一個ReadOnlyCollection<T>,然後在包裝器數組中設置一個值。

的BCL擁有門面不變性出色的接口,如IReadOnlyCollection<T>IReadOnlyList<T>,甚至IEnumerable<T>等。但是,我覺得觀察不變性也是有用的,據我所知,有沒有在BCL carring任何接口這個意思(如果我錯了,請指出他們)。這是不存在的,因爲這種不變性的形式不能通過接口聲明來實現,只能由實現者來實現(儘管接口可以承載語義,正如我將在下面展示的那樣)。除此之外:我很想在未來的C#版本中擁有這種能力!


實施例:(可被跳過)我不得不頻繁地執行該獲取作爲參數,它是由另一個線程以及一個集合的方法,但該方法需要收集期間不被修改其執行,因此我聲明該參數的類型爲IReadOnlyCollection<T>,並給我自己一個認爲我已滿足要求的背後拍拍。錯誤...對於一個調用者來說,簽名看起來好像該方法承諾不會改變集合,沒有別的,如果調用者對文檔(外觀)進行第二種解釋,他可能會認爲允許變異並且方法在問題是抵抗的。雖然這個例子還有其他更傳統的解決方案,但我希望你能看到這個問題可能是一個實際問題,特別是當其他人使用你的代碼(或者未來 - 你就是這個問題)時。


所以現在我的實際問題(這引發懷疑現有的接口語義):

我想用觀測不變性和門面不變性和區分它們。我想到的兩個選項是:

  1. 每次使用BCL接口和文檔,無論是觀察還是隻是外觀不變性。缺點:使用這些代碼的用戶只有在文件已經太晚時纔會查閱文檔,即發現錯誤時。我想引導他們進入成功之地;文檔不能這樣做)。此外,我發現這種語義足夠重要,可以在類型系統中看到,而不僅僅是在文檔中。
  2. 定義攜帶的觀測不變性語義明確,像IImmutableCollection<T> : IReadOnlyCollection<T> { }IImmutableList<T> : IReadOnlyList<T> { }接口。請注意,接口除了繼承的接口外沒有任何成員。這些接口的目的僅僅是說「即使聲明類型也不會改變我!」•我特別說「不會」,而不是「不能」。這裏有一個缺點:一個邪惡(或者錯誤的,保持禮貌)的實現者不會被編譯器或其他任何東西阻止破壞這個合約。然而,優點是程序員選擇實現這個接口而不是它直接繼承的接口,它很可能意識到這個接口發送的額外消息,因爲程序員知道這個接口的存在,可能會相應地實施。

我想與第二個選項去,但恐怕它有設計問題媲美的委託類型(其被髮明結轉他們semanticless同行FuncAction語義信息),並以某種方式失敗,見例如here

我想知道你是否也遇到過/討論過這個問題,或者我是否只是在過多地討論語義,應該接受現有的接口,以及我是否不知道現有的解決方案BCL。任何像上面提到的設計問題都會有所幫助。但是我特別感興趣的是你可能會提出的其他解決方案(這些解決方案區分了聲明和使用中的觀察和外觀不變性)。
預先感謝您。


†我忽略了字段等對集合元素的變化。
‡這對我之前給出的例子有效,但聲明實際上更廣泛。例如任何聲明的方法不會改變它,或這種類型的參數傳遞,可以在方法需要收集未在其執行過程中改變(這是不同說該方法不能改變集合,這是唯一的可以使用現有的接口聲明)以及可能還有其他接口。

+1

這或許是相關的:HTTP://blogs.msdn。com/b/bclteam/archive/2012/12/18/preview-of-immutable-collections-released-on-nuget.aspx –

+6

我贊成使用術語「不可變」作爲觀察上不可變集合的類名的一部分,和「Readonly」作爲可變集合的不可變外觀的類名的一部分。我還喜歡名稱中帶有「不可變」一詞的非集合類,它們是「非常不可變的」,即字段都是私有隻讀值類型,或者是本身是不可變的私有隻讀引用類型。 ('readonly'就是C#關鍵字)(這似乎也是微軟採用的慣例,非常快樂的日子。) –

+0

我並不是真正希望你在問什麼,但是IImmutablexxx 意味着它不會改變(它可以改變,但是更改將生成一個新的集合)IReadOnlyxxx僅防止收件人更改提供者可以更改的集合。 –

回答

1

一個接口不能保證不變性。名稱中的單詞不會阻止可變性,這只是文檔的另一個提示。

如果你想要一個不可變對象,需要一個具體的類型,它是不可改變的。在c#中,不變性是依賴於實現的,並且在接口中不可見。

正如克里斯說,你可以找到existing implementations of immutable collections

+0

除了一些「標記」接口外,任何*接口唯一可以保證的是,任何實現它的類都將遵守接口的合約或被視爲「破壞」的實現。如果'ImmutableFloatMatrix'的合約指定每個對'GetValue(X,Y)'的調用必須總是返回給定的(X,Y)對的相同結果,那麼消費者將同樣有權假定'ImmutableFloatMatrix '是不變的,而不是假設通過「Fred」列表.Add'不會追加「Barney」。 – supercat