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

Eric Lippert告诉我,我应该“尽量使价值类型不可变”,所以我想我应该尽量使价值类型不变。

但是,我在System.Web程序集中发现了这个内部可变结构System.Web.Util.SimpleBitVector32 ,这让我认为有一个可变结构必须有一个很好的理由。 我猜测他们这样做的原因是因为它在测试中表现更好,并且它们保持内在,以防止其被滥用。 但是,这是猜测。

我已经C&P'd这个结构的来源。 这是什么证明设计决定使用可变结构? 一般来说,这种方法可以获得何种好处,何时这些好处足以证明潜在的损害?

[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;
    }
}

实际上,如果你在.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 Shared Source 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位整数,我可以说这很容易被写成一个不可变的结构,可能对性能没有影响。 无论你是在调用一个改变32位字段值的变异方法,还是用新的32位结构替换32位结构,你仍然在执行完全相同的内存操作。

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

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

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


    SimpleBitVector32是可变的,我怀疑,因为BitVector32是可变的相同原因。 在我看来,不可改变的准则就是这样一个准则 ; 然而,应该有一个很好的理由这样做。

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

    可变性必须有意义。 我同意@JoeWhite:有人想要一种像数组一样的行为(虽然实际上只是32位整数中的位)。 还有BitVector的结构很容易就是......不可变的。

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

    编辑
    为了记录,我真心地同意你应该总是试图让一个结构不可变。 如果您发现需求决定了成员的可变性,请重新审视设计决策并让同行参与其中。

    更新
    在考虑可变值类型与不可变时,我最初并没有对我的性能评估充满信心。 不过,正如@大卫指出的那样,Eric Lippert写道:

    有时你需要从系统中抽出性能的最后一点。 在这些情况下,您有时必须在干净, 纯粹 ,健壮,可以理解,可预测,可修改的代码和代码之间进行权衡,这些代码完全不是上面的,而是非常快速的。

    我粗体显示因为可变结构不符合结构应该是不可变的纯理想。 编写一个可变结构会产生副作用:正如Eric继续解释的那样,可理解性和可预测性会受到影响:

    可变的值类型......行为的方式使得许多人发现它们很不直观,因此可以很容易地编写错误的代码(或者正确的代码很容易变成错误的代码)。但是,它们真的很快。

    Eric所做的一点是,作为设计师和/或开发人员,您需要作出有意识和明智的决定。 你如何获得消息? 埃里克也解释说:

    我会考虑编写两个基准测试解决方案 - 一个使用可变结构,一个使用不可变结构 - 并运行一些真实的基于用户场景的基准测试。 但事情是这样的:不要选择更快的。 相反,在您运行基准测试之前,请先决定如何慢得让人无法接受

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

    如果两种解决方案都可以接受,请选择干净,正确和快速的解决方案。

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

    链接地址: http://www.djcxy.com/p/28051.html

    上一篇: Why is it okay that this struct is mutable? When are mutable structs acceptable?

    下一篇: Object reference not set to an instance of an object in ASP.NET