簡體   English   中英

為什么這個結構可變是可以的? 何時可以接受可變結構?

[英]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.Collections.Specialized.BitVector32(唯一的一個公共...)
  • System.Web.Util.SafeBitVector32(線程安全)
  • System.Web.Util.SimpleBitVector32
  • System.Runtime.Caching.SafeBitVector32(線程安全)
  • System.Configuration.SafeBitVector32(線程安全)
  • System.Configuration.SimpleBitVector32

如果您在這里找到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位向量是合理的,但有一些警告:

  1. 我建議在對結構執行任何更新時使用Interlocked.CompareExchange循環,而不是直接使用普通的布爾運算符。 如果一個線程嘗試寫入第3位,而另一個線程嘗試寫入第8位,則除了延遲一點點之外,其他任何操作都不會干擾另一個線程。 使用Interlocked.CompareExchange循環將避免發生錯誤行為的可能性(線程1讀取值,線程2讀取舊值,線程1寫入新值,線程2寫入基於舊值計算的值並撤消線程1的更改),而無需任何操作其他類型的鎖定。
  2. 應該避免使用除屬性設置器之外的修改“ this”的結構成員。 最好使用將結構作為參考參數的靜態方法。 從語義和性能的角度來看,調用修改“ this”的結構成員通常與調用將該成員作為參考參數的靜態方法相同,但是有一個主要區別:如果嘗試傳遞只讀結構通過引用靜態方法,將得到一個編譯器錯誤。 相反,如果在只讀結構上調用修改“ this”的方法,則不會有任何編譯器錯誤,但是不會發生預期的修改。 由於即使可變結構在某些情況下也可以被視為只讀,因此發生編譯器錯誤要比使代碼能夠編譯但無法正常工作要好得多。

埃里克·利珀特(Eric Lippert)喜歡講究可變結構的邪惡性,但重要的是要認識到他與可變結構的關系:他是C#團隊的一員,負責提供諸如閉包和迭代器之類的語言支持功能。 由於.net創建初期的一些設計決策,因此在某些情況下很難正確支持值類型語義。 如果沒有任何可變的值類型,他的工作將容易得多。 我並不討厭Eric的觀點,但是需要注意的是,一些在框架和語言設計中可能很重要的原則不適用於應用程序設計。

如果我理解正確,則不能僅通過使用SerializableAttribute來創建可序列化的不可變結構。 這是因為在反序列化期間,序列化程序實例化該結構的默認實例,然后在實例化之后設置所有字段。 如果它們是只讀的,則反序列化將失敗。

因此,該結構必須是可變的,否則將需要一個復雜的序列化系統。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM