不可变的只读引用类型和FXCop违规:不要声明只读可变引用类型

我一直试图绕过FXCop违规行为“DoNotDeclareReadOnlyMutableReferenceTypes”

MSDN: http : //msdn.microsoft.com/en-us/library/ms182302%28VS.80%29.aspx

来自MSDN的代码会导致此违规:

namespace SecurityLibrary { public class MutableReferenceTypes { static protected readonly StringBuilder SomeStringBuilder; static MutableReferenceTypes() { SomeStringBuilder = new StringBuilder(); } } } 

从Jon 在这里和这里的答案,我理解持有对象引用的字段(在本例中为SomeStringBuilder)是readonly而不是对象本身(由new StringBuilder()创建)

所以以这个例子为例,一旦该字段引用它,我将如何更改对象本身? 我喜欢Eric Lippert关于如何更改readonly数组的示例 ,并希望看到类似于任何其他可变引用类型的内容

由于MutableReferenceTypes类出现在问题中,因此SomeStringBuilder字段是私有的,因此无法从任何外部调用者实际改变它。

但是,这个class级本身可以改变这个领域。 它目前没有,但它可以在以后的迭代中。

这是一个示例方法:

 public static void Mutate() { SomeStringBuilder.AppendLine("Foo"); } 

调用Mutate方法会改变类,因为SomeStringBuilder现在已经改变了。

不变性不仅涉及代码的当前版本,还涉及保护自己免受未来错误的影响。 并非所有类都需要是不可变的,但如果您选择创建不可变类型,则保持一致是最安全的。

readonly意味着您无法在构建后更改参考。

官方FXCop的立场是,它建议只有那些无法修改的类型才能被声明为readonly 。 因此,像string这样的东西是可以的,因为无法更改对象的值。 但是, StringBuilder的值可以更改,但只读它只会阻止您将字段分配给不同的StringBuilder实例,或者在构造函数运行后为null

我不同意FXCop对此规则的看法。 只要有人明白这只是一种强制执行,参考可能不会在施工后改变,那么就没有混淆。

请注意,值类型由readonly关键字使不可变,但引用类型不是。

 namespace SecurityLibrary { public class MutableReferenceTypes { static protected readonly StringBuilder SomeStringBuilder; static MutableReferenceTypes() { // allowed SomeStringBuilder = new StringBuilder(); } void Foo() { // not allowed SomeStringBuilder = new StringBuilder(); } void Bar() { // allowed but FXCop doesn't like this SomeStringBuilder.AppendLine("Bar"); } } } 

.Net有一个允许的不可变引用类型列表, StringBuilder不是其中之一。

抱怨是你正在构建的东西不是不可变的,虽然静态构造函数被调用一次并且类被初始化一次,所有这些都保持不变,其余的是可变的。 一个线程可以调用.Append() ,然后另一个…你看到字符串构建器本身如何变异并且不是真正的readonly ,因为它不断地改变状态/变异。

readonly声明为真是一个误称,因为那里引用的对象本身就在不断变化。

您无法更改引用,但对(可变)对象的任何调用都会更改其状态。

因此,由于SomeStringBuilder (在本例中)本身是可变的,因此其内容可能会发生变化,这可能会误导类的用户,因为它实际上并不是“只读”。

基本上, readonly不以任何方式保证对象不会改变,它只是说引用不会改变。

您不会更改对象的值。 这就是规则的要点。 声明为readonly的任何字段都应该是readonly。 具有只读可变参考是矛盾的。 如果你可以改变字段“指向”的值,那么它不再是真正的只读。 将某个对象A的所有成员的值分配给由某个字段表示的某个对象B或者只是将A分配给该字段(当它们属于同一类型时)之间确实没有function差异,但是当该字段中只有一个是有效的是只读的,但因为你可以有效地改变字段所代表的值的价值,因为它已经说明并不是真正的只读