为什么这个结构是可变的? 什么时候可变结构可以接受?
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.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