2009-05-26 62 views
8

我想知道如何定義不變性?如果這些價值觀不公開,所以不能修改,那就夠了?不變性的真正定義?

可以在類型內修改值,而不是類型的客戶?

或者只能在構造函數中設置它們嗎?如果是這樣,在雙重初始化的情況下(在結構體上使用this關鍵字等)對於不可變類型仍然可以嗎?

我該如何保證類型是100%不變的?

回答

23

如果這些值沒有公開顯示,所以不能修改,那就夠了?

不,因爲您需要閱讀權限。

可以在類型內修改值,而不是類型的客戶?

不,因爲這仍然是突變。

或者一個人只能在一個構造函數中設置它們嗎?

丁丁丁!另外一點是,不可變類型通常具有構造並返回新實例的方法,並且還經常具有特別爲這些方法使用而標記爲internal的額外構造函數。

我該如何保證該類型是100%不變的?

在.Net中得到這樣的保證是非常棘手的,因爲你可以使用反射來修改(改變)私有成員。

+2

只是涵蓋了一切;- – 2009-05-26 21:50:22

+0

嘿馬克,我很驚訝不會看到你我最近的不變問題。不變性讓我想起你:) – 2009-05-26 22:26:19

3

這裏是不可改變的維基百科(link

定義中的「在面向對象和功能編程,一個不可變的對象是一個對象,其狀態被創建後無​​法修改」。

基本上,一旦對象被創建,它的任何屬性都不能被改變。一個例子是String類。一旦創建了一個String對象,它就不能被改變。對它所做的任何操作實際上都會創建一個新的String對象。

1

我已經瞭解到,不變性是當您在構造函數中設置所有內容並且無法在對象的生命週期中對其進行修改時。

1

不變性的定義可以位於Google

例子:

不變 - 從字面上看,無法更改。
www.filosofia.net/materiales/rec/glosaen。htm

就不可變數據結構而言,典型的定義是一次寫入多次讀取,換句話說,正如您所說,一旦創建,就不能更改。

有些情況下,在灰色地帶略有。例如,.NET字符串被認爲是不可變的,因爲它們不能改變,但是,StringBuilder內部修改了一個String對象。

1

一個不可變的本質上是一個強制自己在自己的代碼中是最終的類。一旦它在那裏,什麼都不能改變。據我所知,事情是在構造函數中設置的,然後就是這樣。我看不出有什麼可能是不可改變的。

3

很多問題在那裏。我會盡力回答他們每個人:

  • 「我想知道如何定義不變性? - 直接從Wikipedia page(和一個完全精確的/簡明的定義)

    的不可變的對象是一個對象,其狀態被創建

  • 後「如果值不被暴露不能被修改作爲公衆,所以不能修改,那就夠了?「 - 不完全的。它不能在中以任何方式修改,因此您必須確保方法/函數不會更改對象的狀態,並且如果執行操作,則始終返回新實例。

  • 「可以在類型內修改值,而不是類型的客戶?」 - 從技術上講,它不能在內部或由該類型的消費者修改。在實踐中,存在幾種類型,如System.String(這是一個關於此事的參考類型),儘管不是理論上,但它幾乎可以被認爲是可變的。

  • 「或者只能在構造函數中設置它們嗎?」 - 是的,理論上這只是只有可以設置狀態(變量)的地方。

  • 「如果是這樣,在雙重初始化的情況下(在結構體上使用this關鍵字等)對於不可變類型仍然可以嗎?」 - 是的,這仍然非常好,因爲它都是初始化(創建)過程的一部分,而實例在完成之前不會返回。

  • 「我怎樣才能保證類型是100%不變的?」 - 以下條件應該確保。 (有人請指出我是否遺漏了一個。)

    1. 不要暴露任何變量。他們應該全部保持private(即使protected也不可接受,因爲派生類可以修改狀態)。
    2. 不允許任何實例方法修改狀態(變量)。這應該只在構造函數中完成,而方法應該使用特定構造函數創建新實例,如果它們需要返回「修改」對象的話。
    3. 由方法返回的所有成員(作爲只讀對象)或對象必須自己是不可變的。

    注意:您不能確保派生類型的不變性,因爲它們可以定義新變量。這是標記任何類型的原因,以確保它不可變爲sealed,以便在代碼中的任何位置都不能將派生類視爲基類不可變類型。

希望有所幫助。

+1

我會補充一點,如果你暴露成員,他們可能也應該是不變的... – 2009-05-26 21:55:06

+0

@Denis:是的,我知道我錯過了一些明顯的東西。謝謝! – Noldorin 2009-05-26 22:06:15

7

以前的海報已經聲明,您應該在構造函數中爲您的字段賦值,然後將手放在它們上面。但有時候說起來容易做起來難。假設您的不可變對象暴露了List<string>類型的屬性。該列表是否允許更改?如果不是,你將如何控制它?

Eric Lippert在他的博客中寫了一系列關於C#中的不變性的帖子,您可能會感興趣:you find the first part here

5

我認爲在所有這些答案中可能會錯過的一件事是,我認爲即使內部狀態發生變化,對象也可以被認爲是不可變的 - 只要這些內部變化對'客戶'代碼不可見即可。

例如,System.String類是不可變的,但我認爲它將被允許緩存一個實例的哈希代碼,所以哈希僅在第一次調用GetHashCode()時計算。請注意,據我所知,System.String類確實而不是這樣做,但我認爲它可以仍然被認爲是不可變的。當然,這些變化中的任何一個都必須以線程安全的方式進行處理(以符合變化的不可觀察的方面)。

說實話,我想不出有很多理由可能需要或需要這種'隱形可變性'。

1

不幸的是,在c#/ vb.net中沒有不可變的關鍵字,儘管它已經被討論過了,但是如果沒有自動屬性並且所有的字段都是隻讀的(readonly字段只能在構造函數中分配),修改後的字段被聲明爲不可變類型,你將確保你的自我不變。

1

不可變對象是一個可觀察狀態永遠不會被任何合理的代碼執行序列所改變的對象。一個不可變的類型是保證任何暴露於外部世界的實例都是不可變的(這個要求通常被認爲是要求對象的狀態只能在其構造函數中設置;對於對象而言這並不是必須的私人構造函數,對於在施工期間自行調用外部方法的對象來說也不夠)。

然而,其他答案忽略的一點是對象狀態的定義。如果Foo是一類,則List<Foo>的狀態由包含在其中的對象標識的序列組成。如果僅通過代碼保存對特定List<Foo>實例的引用,該代碼既不會導致該序列被更改,也不會將其暴露給可能會這樣做的代碼,那麼該實例將是不可變的,無論其中涉及的Foo對象是否可變或不可變的。

要使用一個類比,如果有一張汽車VIN(車輛識別號碼)列表打印在防篡改紙上,即使車輛沒有,列表本身也是不可改變的。即使今天列表中包含十輛紅色汽車,明天可能會包含十輛藍色汽車;然而,他們仍然會是相同的十輛車。