为什么这个结构是可变的可以吗?什么时候可变结构可以接受?

sma*_*man 35 .net c# performance struct immutability

Eric Lippert告诉我,我应该"尝试始终使值类型不可变",所以我认为我应该尝试始终使值类型不可变.

但是,我刚刚System.Web.Util.SimpleBitVector32System.Web程序集中找到了这个内部可变结构,这让我觉得必须有一个很好的理由来拥有一个可变结构.我猜他们这样做的原因是因为它在测试中表现得更好,而且他们把它保持在内部以阻止它的误用.然而,这是猜测.

我已经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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Joe*_*ite 19

鉴于有效负载是一个32位整数,我会说这很容易被写成一个不可变的结构,可能对性能没有影响.无论您是调用更改32位字段值的mutator方法,还是使用新的32位结构替换32位结构,您仍然可以执行完全相同的内存操作.

可能有人想要的东西就像一个数组(虽然实际上只是一个32位整数的位),所以他们决定使用它的索引器语法,而不是一个不太明显的.WithTheseBitsChanged()方法返回一个新的结构.由于它不会被MS的网络团队以外的任何人直接使用,甚至可能不是很多人甚至在网络团队中使用,我认为他们在设计决策方面比构建公共API的人有更多的余地.

所以,不,可能不是那种表现方式 - 它可能只是程序员在编码风格方面的个人偏好,并且从来没有任何令人信服的理由来改变它.

如果您正在寻找设计指南,我不会花太多时间查看尚未被公开消费的代码.

  • 关于寻找内部代码以获取指导的评论+1 (2认同)

IAb*_*act 15

SimpleBitVector32我怀疑是可变的,因为同样的原因BitVector32是可变的.在我看来,不变的准则只是一个指导原则 ; 但是,人们应该有充分的理由这样做.

还要考虑一下Dictionary<TKey, TValue>- 我在这里讨论一些扩展细节.字典的Entry结构是可变的 - 您可以随时更改TValue.但是,Entry逻辑上代表一个.

可变性必须有意义.我同意@JoeWhite:有人想要的东西就像一个数组(虽然真的只是一个32位整数的位) ; 另外,两个BitVector结构都很容易......不可变.

但是,作为一个全面的陈述,我不同意它可能只是一些程序员在编码风格上的个人偏好而更倾向于从未 [也没有] 任何令人信服的理由来改变它.只需了解并理解使用可变结构的责任.

编辑
为了记录,我衷心同意你应该总是尝试使结构不可变.如果您发现需求决定了成员的可变性,请重新审视设计决策并让同事参与进来.

更新
我在考虑可变值类型v.不可变时,最初对我的性能评估没有信心.然而,正如@David指出的那样,Eric Lippert写道:

有时你需要从系统中榨取最后一点性能.在这些情况下,您有时必须在干净,纯粹,健壮,可理解,可预测,可修改的代码和代码之间进行权衡,这些代码不是上述但速度极快.

我加粗纯粹因为一个可变结构不符合结构应该是不可变的纯理想.编写可变结构有副作用:可理解性和可预测性受到损害,正如Eric继续解释:

可变值类型......的行为方式让很多人发现非常违反直觉,从而可以很容易地编写错误的代码(或者正确的代码很容易变成错误的代码.)但是,它们真的很快.

Eric所提出的观点是,您作为设计师和/或开发人员需要做出有意识且明智的决定.你怎么知道的?埃里克解释说:

我会考虑编写两个基准测试解决方案 - 一个使用可变结构,一个使用不可变结构 - 并运行一些 现实的用户场景为基准.但事情就是这样:不要选择更快的一个.相反,决定在运行基准测试之前,速度慢得多慢得令人无法接受.

我们知道,改变的值类型不是创建一个新的价值型快; 但考虑到正确性:

如果两种解决方案都可以接受,请选择干净,正确且足够快的解决方案.

关键是要足够以抵消选择mutable而不是不可变的副作用.只有你可以确定.


Sim*_*ier 15

实际上,如果你在.NET框架中搜索包含BitVector的所有类,你会发现一堆这些野兽:-)

  • System.Collections.Specialized.BitVector32(唯一公开的...)
  • System.Web.Util.SafeBitVector32(线程安全)
  • System.Web.Util.SimpleBitVector32
  • System.Runtime.Caching.SafeBitVector32(线程安全)
  • System.Configuration.SafeBitVector32(线程安全)
  • System.Configuration.SimpleBitVector32

如果你看这里是SSCLI(微软共享源CLI,又名ROTOR)的System.Configuration.SimpleBitVector32源代码,你会发现这个评论:

//
// 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
Run Code Online (Sandbox Code Playgroud)

我相信这说明了一切.我认为System.Web.Util更复杂,但建立在相同的基础上.