[英]Why is it okay that this struct is mutable? When are mutable structs acceptable?
埃里克·利珀特(Eric Lippert)告訴我,“應該始終使值類型不可變” ,因此,我認為我應該盡量使值類型不可變。
但是,我只是在System.Web
程序集中找到了這個內部可變結構System.Web.Util.SimpleBitVector32
,這使我認為擁有可變結構必定有充分的理由。 我猜他們這樣做的原因是因為它在測試中表現更好,並且將其保留在內部以防止誤用。 但是,這是推測。
我已經C&P成為了這個結構的來源。 是什么證明使用可變結構的設計決定合理? 通常,通過這種方法可以獲得什么樣的收益?這些收益何時足夠顯着,足以證明潛在的危害?
[Serializable, StructLayout(LayoutKind.Sequential)]
internal struct SimpleBitVector32
{
private int data;
internal SimpleBitVector32(int data)
{
this.data = data;
}
internal int IntegerValue
{
get { return this.data; }
set { this.data = value; }
}
internal bool this[int bit]
{
get {
return ((this.data & bit) == bit);
}
set {
int data = this.data;
if (value) this.data = data | bit;
else this.data = data & ~bit;
}
}
internal int this[int mask, int offset]
{
get { return ((this.data & mask) >> offset); }
set { this.data = (this.data & ~mask) | (value << offset); }
}
internal void Set(int bit)
{
this.data |= bit;
}
internal void Clear(int bit)
{
this.data &= ~bit;
}
}
鑒於有效負載是32位整數,我想說這很容易寫成一個不變的結構,可能對性能沒有影響。 無論您是要調用更改32位字段值的mutator方法,還是用新的32位結構替換32位結構,您仍在執行完全相同的內存操作。
可能有人想要某種類似於數組的東西(雖然實際上只是32位整數中的位),所以他們決定要使用indexer語法,而不是不太明顯的.WithTheseBitsChanged()方法,該方法返回一個新的結構。 由於MS網站團隊之外的任何人都不會直接使用它,甚至Web團隊中也不會有很多人使用它,因此我想他們在設計決策上比建立公共API的人還有很多余地。
因此,不,可能不是那樣的性能-這可能只是某些程序員在編碼樣式上的個人偏愛,並且從來沒有任何令人信服的理由來更改它。
如果您正在尋找設計指南,那么我不會花太多時間來查看尚未完善的代碼以供公眾使用。
SimpleBitVector32
是可變的,我懷疑,出於同樣的原因, BitVector32
是可變的。 我認為, 不變的指導方針就是這樣的指導方針 ; 但是,這樣做應該有一個很好的理由 。
同樣,請考慮Dictionary<TKey, TValue>
-我在這里進一步擴展了一些細節 。 字典的Entry
結構是可變的-您可以隨時更改TValue。 但是, Entry
邏輯上表示一個值 。
可變性必須有意義。 我同意@JoeWhite的觀點: 有人想要某種像數組一樣的東西(而實際上只是32位整數中的位) ; 而且兩個BitVector結構都可以很容易...是不可變的 。
但是,作為一個籠統的聲明,我不同意這可能只是某些程序員在編碼風格上的個人偏愛,並且更傾向於從未 (也沒有)有任何令人信服的理由來更改它 。 只需了解並了解使用可變結構的責任。
編輯
作為記錄,我衷心同意您應該始終嘗試使結構不變。 如果發現需求決定成員的可變性,請重新訪問設計決策並讓同行參與。
更新資料
當考慮可變值類型v。不可變時,我最初對性能評估沒有信心。 但是,正如@David指出的那樣, 埃里克·利珀特(Eric Lippert)寫道 :
有時候,您需要將系統的所有性能發揮到極致。 在這些情況下,有時您必須在干凈, 純凈 ,健壯,可理解,可預測,可修改的代碼與上述所有都不是,但速度很快的代碼之間進行權衡。
我加粗了pure,因為可變的結構不適合結構應該是不可變的純凈理想。 編寫可變結構存在副作用:隨着Eric繼續解釋,不穩定和可預測性受到損害。
可變值類型...的行為方式使許多人感到與直覺背道而馳,因此可以輕松編寫錯誤代碼(或正確的代碼,而這些代碼很容易被意外地變成錯誤代碼。)但是,是的,它們的確非常快。
Eric提出的觀點是,作為設計師和/或開發人員,您需要做出自覺且明智的決定。 您如何了解情況? 埃里克還解釋說:
我會考慮編寫兩種基准解決方案-一種使用可變結構,一種使用不可變結構-並運行一些針對用戶場景的實際基准。 但這就是問題:不要選擇速度更快的產品。 相反,在運行基准測試之前,請先確定多慢是不可接受的 。
我們知道,改變的值類型是不是創建一個新的價值型快; 但考慮正確性 :
如果兩種解決方案均可接受,請選擇一種干凈,正確且足夠快的解決方案。
關鍵在於足夠快地抵消選擇可變而不是不變的副作用。 只有您可以確定。
實際上,如果您在.NET框架中搜索所有包含BitVector的類,則會發現許多此類野獸:-)
如果您在這里找到System.Configuration.SimpleBitVector32的SSCLI(Microsoft共享源CLI,又名ROTOR)源,則會發現以下注釋:
//
// This is a cut down copy of System.Collections.Specialized.BitVector32. The
// reason this is here is because it is used rather intensively by Control and
// WebControl. As a result, being able to inline this operations results in a
// measurable performance gain, at the expense of some maintainability.
//
[Serializable()]
internal struct SimpleBitVector32
我相信這說明了一切。 我認為System.Web.Util比較復雜,但基於相同的理由。
如下所示,將結構用於32位或64位向量是合理的,但有一些警告:
埃里克·利珀特(Eric Lippert)喜歡講究可變結構的邪惡性,但重要的是要認識到他與可變結構的關系:他是C#團隊的一員,負責提供諸如閉包和迭代器之類的語言支持功能。 由於.net創建初期的一些設計決策,因此在某些情況下很難正確支持值類型語義。 如果沒有任何可變的值類型,他的工作將容易得多。 我並不討厭Eric的觀點,但是需要注意的是,一些在框架和語言設計中可能很重要的原則不適用於應用程序設計。
如果我理解正確,則不能僅通過使用SerializableAttribute
來創建可序列化的不可變結構。 這是因為在反序列化期間,序列化程序實例化該結構的默認實例,然后在實例化之后設置所有字段。 如果它們是只讀的,則反序列化將失敗。
因此,該結構必須是可變的,否則將需要一個復雜的序列化系統。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.