2011-08-17 41 views
9

我有一個開始生命是可變的類,但我從此使它不可變。我是否應該將其更改爲struct?在選擇一個之前需要考慮什麼?我的例子是一個Point類型類(它表示一個自定義座標系中的座標),它由4個int字段組成,還有一些屬性用於以不同的方式訪問相同的數據。我注意到String是一個類,是不可變的,所以必須有一些用例。不可變的類與不可變的結構

回答

5

一般來說,不,你不應該把它改成結構。僅僅因爲它是不可變的並不意味着它自動成爲一個結構的好候選者。

結構應該很小。使用你的四個整數,它只是建議的16字節的限制,結構的性能開始下降。

一個結構體應該代表某種單一的實體。也許你的班級這樣做,但從你的描述來看,這聽起來不太可能。

結構比類更難正確實施。它應該比較比較好,所以要實現的內容比類中預期的要多。

除非你有一個很好的理由將它作爲一個結構來實現,比如性能問題,否則你應該把你的類保留爲一個類。

+0

我打算接受這個答案,因爲它在一般意義上是最有用的。不過,我認爲我會在接受不使用結構的建議之前進行一些性能測試。該課程確實代表一個單一實體:空間中的一個點。價值平等是有道理的。此外,這是一個遊戲,並將在任何地方使用(請參閱:空間點的表示),因此性能是一個問題。此外,你不可能知道的東西:我可以_probably_(需要測試)只使用2'int'和2'byte',這會減少結構體的大小。 –

+0

好像你所描述的是非常適合成爲一個結構,我會建議實施IComparable的,IEquatable 如果你要在任何集合使用它 – BrandonAGr

0

這實際上取決於你想要/需要代表它所包含的數據。結構通常用於簡單表示或使用to來保存可能需要反覆使用的多個值,並且當對象本身具有需要完成的某些必需行爲時應使用類。在你的情況下,爲了簡單起見,並允許你保留你的財產,你不妨做這個課。

不變性在某些情況下使用。例如,如果你希望某件事物在每次改變時都成爲一個全新的對象,那麼你可能要考慮不變性(數據限制等)。如果你希望能夠傳遞一個你不想改變的特定信息(也許DTO對象來自一個只創建並且沒有改變的傳遞數據庫),這樣你就不會有奇怪的行爲有人改變了一個價值。

+0

結構仍然可以使用屬性,所以這不是將它作爲類保留的參數。 –

+0

我仍然建議保持它作爲一個類,因爲哎呀爲什麼不呢?如果在任何時候您需要添加功能,您必須返回並將其從結構更改爲類,然後添加功能,並且兩者在C#中的行爲幾乎相同。作爲一名C程序員,我喜歡結構,但是在C#中,即使對於有數據的簡單對象,我也傾向於不使用它們,因爲至少對我來說維護這種方法更簡單。 –

4

據我所知,使用結構而不是類是一個優化問題。結構存儲在堆棧而不是堆上,所以它們非常適合作爲大量使用的多個字段的輕量級包。無論何時在方法之間傳遞,它們都會被複制,這使得傳遞它們更加昂貴 - 但是這可能適合您對不變性的渴望。使用結構的缺點包括語言施加的自然限制。

我根本就不是這方面的專家,但想把這方面提升。有更多經驗的人當然應該介入並擴展這一點。另外,這裏有一個有用的討論:When to use struct?

最終我會說如果不變性是你唯一的考慮,保持它的一個類(特別是現在,我看到別人表達這種觀點)。無論結構如何,都可以實現不變性,所以這個問題似乎沒有強烈關聯。

+0

至少在我看來,幾乎所有的'struct'都是不可變的。我意識到情況並非如此;並非所有不可變對象都是'struct'。但在我做這件事時,思考這件事似乎是件好事。 –

+0

這是一個很好的問題+1。我絕對從閱讀其他文章中學到了東西。就像我說的那樣,如果不變性是唯一的考慮因素,則無需切換。但從上面的評論看來,它畢竟可能值得考慮。 –

1

保持它作爲一個類,除非它足夠小,結構纔有意義。這將是罕見的。我的想法是,一般來說,「結構」的情況正在迅速消失。如果在語言開始的時候更強大的語義(不變性和域值對象特徵),我會改變主意。作爲一個相當新的例子,考慮System.Tuple <>。也就是說,我會問,你的對象(修辭地說)略有對衝。

+0

在問題中解釋了什麼:「我的特殊情況是由4個」int「字段組成的一個Point類型類(它表示一個自定義座標系中的座標),以及一些訪問相同的數據以不同的方式。「 –

+0

如果我沒有通過將Point聲明爲一個結構體而損失任何東西,只是因爲一個點除了它的所有座標之外沒有其他身份,並且因爲人們(程序員)傾向於爲結構假設不變性。否則,我會堅持讓它上課。 – Kit

0

我不會改變爲一個結構不變性。結構和類之間最大的區別是結構是一個值類型,而類是引用類型。傳遞給方法時會複製結構體,但傳遞給方法的類將通過引用傳遞。類和事物一樣可以是不可變的。

2

如果它很小且不可變,那麼使它成爲一個結構將是可取的。特別是如果你打算收集大量的點數。如果您擁有100萬個點的列表,那麼就GC時間和每個對象實例的額外開銷的16個字節而言,所有這些對象都會有大量開銷。

字符串必須是引用類型,因爲您不能在堆棧中存儲可變數量的字符,更不用說實習字符串和共享實例的能力。這msdn article有關於爲什麼Tuple被作爲參考類型的信息。

+0

+1和有趣的文章 –

0

不可變結構和密封不可變類類型對象的語義幾乎完全相同。除非這些結構類型化爲Object或接口類型,否則16個字節或更小的結構的性能一般會優於其他相同類類型對象的性能;這種類型轉換會大大降低結構的性能,但不會特別影響類類型的性能。需要注意的是有巨大區別的:

 
    void doSomethingWithIFoo(IFoo whatever); 

 
    void doSomethingWithIFoo<T>(T whatever) where T:IFoo; 

傳遞一個結構,前者將要求結構類型強制轉換爲接口類型,而第二種形式將允許結構作爲自己的類型來處理。

+0

我想你的意思是:'無效doSomethingWithIFoo (T不管)T:IFoo;'? –

+0

@馬修·沙利:固定。不僅泛型函數不會像我寫的那樣避免裝箱,而且它對於類型推斷也不會很好。 – supercat