2010-02-16 74 views
7

我一直在试图总结我的头围绕此的FxCop侵犯“DoNotDeclareReadOnlyMutableReferenceTypes”不可变的只读引用类型和FxCop的违规:不申报只读可变引用类型

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

代码这将导致此冲突:

namespace SecurityLibrary 
{ 
    public class MutableReferenceTypes 
    { 
     static protected readonly StringBuilder SomeStringBuilder; 

     static MutableReferenceTypes() 
     { 
      SomeStringBuilder = new StringBuilder(); 
     } 
    } 
} 

乔恩的回答herehere,据我所知,该领域保持参照对象(在这种情况下, SomeStringBuilder)是只读的,而不是对象本身(它由new StringBuilder()创建)

因此,考虑这个例子,一旦字段有引用它,我将如何更改对象本身?我喜欢Eric Lippert's example如何更改只读数组,并希望看到类似的任何其他可变参考类型

回答

3

由于MutableReferenceTypes类在问题中提供,因此您无法从任何外部调用者真正改变它,因为SomeStringBuilder字段是私人的。

但是,该类本身可能会改变该字段。它目前不是,但它可能在以后的迭代中。

下面是一个例子方法:

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

调用发生变异的方法,因为SomeStringBuilder现在已经改变会产生变异的类。

不变性不仅是关于您的代码的当前化身,而且还关于保护自己免受未来错误的影响。并不是所有的类都必须是不可变的,但如果选择创建不可变类型,则最安全的做法是保持一致。

+0

不错。太棒了。完善。 – ram 2010-02-16 16:48:22

+2

只是一个小小的错误:该领域是**保护**,而不是私人的,因此它从外部是绝对*可变的。我猜这个*是FXCop反对的。 – 2010-02-16 18:57:11

+0

@Konrad鲁道夫:很好!我只看了第一个关键字并注意到它不是访问修饰符,因此必须默认为默认(原文如此)。我没有注意到关键字已被切换。 – 2010-02-16 19:29:42

0

.Net有一个不可变参考类型列表允许在这里,StringBuilder不是其中之一。

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

声明它readonly确实是一个用词不当,因为在那里引用的对象本身在不断变化。

+1

还应该如何区分将始终指向同一个对象的引用类型字段和可能在包含对象的生命周期中指向不同对象的引用类型字段?不明白参考类型的人可能会感到困惑,但是除非或者直到他学会了解参考类型时,这样的人才会对许多事情感到困惑。 – supercat 2012-11-03 03:50:24

0

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

因此,由于SomeStringBuilder(本例中)本身是可变的,所以它的内容可能会改变,这对于类的用户可能会产生误导,因为它不是真正的“只读”。

基本上,readonly不以任何方式保证对象的敌人没有改变,它只是说,参考不会改变。

+1

引用不会改变的事实对于代码的其他部分可能是至关重要的(例如,某些其他对象可能会获取对StringBuilder的引用并希望能够获取其值);而一个人当然应该意识到被引用的对象可能会被突变,这并不意味着关于声明引用本身不可变是没有任何用处的。顺便说一句,即使代表并不总是不变的;由结构增变器形成的代表会将结构框起来,并且盒装结构将是可变的。 – supercat 2011-01-20 15:52:22

0

您不会更改对象的值。这是规则的要点。声明为只读的字段应该是只读的。有只读的可变参考是一个矛盾。如果你可以改变字段“指向”的值,那么它就不再是只读了。将某个对象A的所有成员的值赋值给某个字段所表示的某个对象B,或者简单地将A赋值给该字段(当它们属于同一类型时)之间确实没有功能上的区别,但是只有其中一个有效时被声明为只读的,但由于您可以有效地改变字段的值,因此已经声明不是真正只读的

5

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"); 
     } 
    } 
} 
+0

代码+1。在MSDN中链接你的代码。如果您早些时候回答过问题,本来会标记为答案。反正好! – ram 2010-02-16 18:17:02

+0

旧的答案,但仍然非常相关。代码分析(就像现在这样)仍然有一些愚蠢的规则(比如这个),假设编码者是白痴。任何人只要懂一点就懂得“只读”的意思。我在设置新项目时所做的第一件事是禁用大量CA规则,例如CA2104。 – 0b101010 2016-07-08 09:37:38

相关问题