2010-10-12 66 views
54

我需要在我的類型上實現只讀屬性。此外,這個屬性的值將在構造函數中設置,並且不會被改變(我正在編寫一個暴露WPF的自定義路由UI命令的類,但沒關係)。如何實現只讀屬性

我看到兩種方法可以做到這一點:

  1. class MyClass 
    { 
        public readonly object MyProperty = new object(); 
    } 
    
  2. class MyClass 
    { 
        private readonly object my_property = new object(); 
        public object MyProperty { get { return my_property; } } 
    } 
    

所有這些FxCop的錯誤,說我不應該有公共成員變量,它似乎是第二個是正確的做法。正確?

在這種情況下,獲取唯一屬性和只讀成員之間是否有區別?

我會很感激任何意見/建議/等。

+20

我有時希望自動屬性語法包括'get;只讀設置;'選項。 – 2010-10-12 18:48:16

+0

[C#,不變性和公共只讀字段]的可能重複(http://stackoverflow.com/questions/2249980/c-immutability-and-public-readonly-fields) – user 2016-02-22 20:46:44

回答

31

版本:
我認爲它並沒有太大的區別,如果你只對源代碼的兼容性感興趣。
使用屬性更適合二進制兼容性,因爲您可以將其替換爲具有setter的屬性,而不會根據您的庫破壞已編譯的代碼。

約定:
您遵守約定。在這種情況下,兩種可能性之間的差異相對較小,因爲公約更好。一個可能會回來咬你的例子是基於反射的代碼。它可能只接受屬性而不接受字段,例如屬性編輯器/查看器。

序列化
從字段到屬性的更改可能會破壞很多序列化器。而AFAIK XmlSerializer只能序列化公共屬性而不是公共字段。

使用的Autoproperty
另一種常見的變化是使用了私人二傳手的autoproperty。雖然這是短暫的,並且它不強制執行只讀屬性。所以我更喜歡其他的。

只讀域selfdocumenting
有外地的一個優勢,但:
它明確在在公共接口,它實際上是不可變的(除非反射)一目瞭然。而在屬性的情況下,你只能看到無法更改它,所以你必須參考文檔或實施。

但說實話,我經常在應用程序代碼中使用第一個,因爲我很懶。在圖書館中,我通常會更加徹底並遵循約定。

C#6.0增加了只讀自動屬性

public object MyProperty { get; } 

所以,當你不需要支持舊的編譯器,你可以有代碼,一個真正的只讀屬性,這只是作爲一個只讀字段簡潔。

49

第二種方法是首選選項。

private readonly int MyVal = 5; 

public int MyProp { get { return MyVal;} } 

這將確保MyVal只能在初始化(也可以在構造函數中設置)進行分配。

正如您已經注意到的 - 這樣您就不會暴露內部成員,允許您在將來更改內部實現。

+0

謝謝。第一個選項也確保相同。您認爲這是首選的原因是什麼? – akonsu 2010-10-12 18:30:58

+0

是否有任何理由我們不應該使用'public int MyProp {get;私人設置; }'?我知道這不是真正的只讀,但它非常接近。 – 2010-10-12 18:54:14

+3

@Mike Hofer - 由於int被聲明爲_readonly_,所以不能在構造函數外改變它。 – Oded 2010-10-12 18:59:35

4

由於封裝,第二種方法是優選的。你當然可以將只讀字段公開,但是這違背了C#習慣用法,在這種習慣中,你通過屬性而不是字段來訪問數據。

這背後的原因是該屬性定義了一個公共接口,如果該屬性的後備實現發生更改,最終不會因爲實現隱藏在接口後面而終止其餘代碼。

5

我同意第二種方式更可取。該偏好的唯一真正原因是.NET類不具有公共字段的一般偏好。但是,如果該字段是隻讀的,那麼除了與其他屬性不一致之外,我不會看到會有什麼真正的反對意見。 readonly字段和get-only屬性之間的真正區別在於readonly字段提供了一個保證,它的值在對象的生命週期中不會改變,並且只讀屬性不會改變。

+0

這是一個很好的解釋。謝謝。 – akonsu 2010-10-12 18:36:06

8

你可以這樣做:

public int Property { get { ... } private set { ... } } 
+4

是的,你可以,但是用這種技術你只能保證該屬性不能被該類的用戶修改,而不能保證該對象在其生命週期內保持不變。 – 2015-05-07 14:36:13

35

通過引入C#6(在VS 2015),現在可以有get -only自動屬性,其中,所述隱式支持字段是readonly(即值可以在構造被分配但不是其他地方):

public string Name { get; } 

public Customer(string name) // Constructor 
{ 
    Name = name; 
} 

private void SomeFunction() 
{ 
    Name = "Something Else"; // Compile-time error 
} 

而且你現在也可以初始化屬性(帶或不帶一個setter)內聯:

public string Name { get; } = "Boris"; 

回到問題參考,這給你選項2的優勢(公共成員是一個屬性,而不是一個領域)與簡潔

不幸的是,它並沒有在公共接口級別提供不變性保證(如@ CodesInChaos關於自我文檔的觀點),因爲對於該類的消費者而言,沒有設置者是無法與私人二傳手區分開來。

+0

關於新語法的更多信息,可以反映並激活私有setter和/或將值加載到後臺字段中(不管訪問修飾符如何)。還可以區分私有setter和缺少setter運行時使用反射。 – 2015-05-28 01:47:28

+1

@Shaun:好點 - 有很多*你可以用反射做的事情!一些可能性與原程序員甚至語言設計者的意圖不符,但是你是否在說只讀域可以用反射來改變(我不知道答案,但似乎有問題)? – 2015-05-28 10:42:11

+2

我並不特別說,不,但答案是:是的..你可以。 https://gist.github.com/wilson0x4d/a053c0fd57892d357b2c如果您認爲存在問題,那麼等待直到您瞭解到地球上的每個操作系統都有一個機制,一個進程可以讀取/寫入任何其他進程的內存足夠的執行權限,也就是說)。這就是爲什麼沒有基於軟件的系統能夠真正安全的原因,但是,我離題了,這與原來的問題沒什麼關係:)即使它很有趣! – 2015-05-30 01:42:37