不要声明只读的可变引用类型 - 为什么?

12

我一直在阅读这个问题以及其他几篇答案,虽然我知道更改引用和更改当前实例的状态之间的区别,但我不确定为什么这意味着我不应该将其标记为只读。这是因为将某些东西标记为只读会告诉编译器实例的某些特殊信息,所以它能够将其视为线程安全,即使实际上可能并不是吗?

也许有些情况下我不希望实例能够被更改,但如果实例的状态发生变化我并不介意(例如单例)。如果我想要这样做,将实例标记为只读会有什么后果?

4个回答

11

没有(基于运行时/环境的)后果。编译器不会崩溃,运行时也不会出问题,一切通常都很好。

只有 FxCop(一些人使用的静态分析工具)才会对此发出警告。链接中解释了警告的原因(从语义上讲,可能不清楚对象实际上并非“只读”,而是变量无法“重新分配”)。

就我个人而言,我不同意这个规则,所以如果你正在运行 FxCop 并且它让你担心,我会禁用它。


谢谢,这就是我想知道的。我只是想确保运行时不会基于标记为readonly的成员做任何假设。 - Sam Holder
阅读了Eric提供的链接文章后,我觉得我不应该忽略这个规则。ReadOnly声明可能会使实现的行为与您预期的完全不同。 - Rafa
@Rafa:如果标记为“readonly”的可变类对象标识字段,则引用的副本将提供对该字段封装状态的实时视图。如果未标记为“readonly”,则引用的副本可能会与该字段封装的状态分离。一些人不知道将“readonly”应用于可变对象的引用时意味着什么,并不意味着它的实际含义没有用处。 - supercat

8
一个有趣的相关点:可变只读结构体是一个坏主意,因为像这样的情况:

http://ericlippert.com/2008/05/14/mutating-readonly-structs/

我向大多数人展示了这段代码,他们无法正确预测其输出。

可变的只读结构体存在一个问题,即人们认为它们正在改变它们,但实际上他们正在改变一个副本。

可变的只读引用类型则存在相反的问题。人们认为它们是深度不可变的,但实际上它们只是浅不可变的。


7

我认为重点在于这可能会误导人——一个不留神的读者可能会假设只读变量实际上是常量,但它并不是。

我认为应该把它视为建议而非规则——肯定有一些情况下可以使用,但需要小心使用。(特别是当可变状态暴露给外部世界时,我会感到紧张。)


4

这两种类型声明都是有效且有用的,尽管它们的含义不同:

  • 对可变对象的常量引用。
  • 对常量对象的可变引用。

如果你真的想要一个对可变对象的常量引用,被迫因任何原因而使用可变引用似乎是不合适的,更不用说是错误的了。在多线程编程中,常量引用可以避免您必须对拥有对象进行同步(您仍然需要通过引用对可变对象进行同步,但这是一种更细粒度的锁定)。

不谨慎的读者可能也是不谨慎的写作者 - 除了以下措施外,无法做太多事情:

const SourceRepository cvs = new ReadOnlyRepository(...);

C++有一种强制指向常量对象的常量指针的方式(引用在C++中略有不同):

Object const* const constantPointerToConstantObject = ...;

这是我在C#/Java中所缺少的几个C++功能之一。

D语言具有更好的关键字(immutable),它强制实现了可传递的不可变性。D的immutable允许编译器进行您担心的类型的优化,并保证安全性。D还支持const用于非传递性const,这意味着传递性和非传递性形式都是独立有用的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接