const和readonly的区别

10
今天我发现了一篇文章,其中一个const字段被称为“编译时常量”,而一个readonly字段被称为“运行时常量”。这两个短语来自《Effective C#》。我在MSDN和语言规范中搜索,未找到关于“运行时常量”的任何信息。
并没有冒犯的意思,但我认为“运行时常量”不是一个合适的术语。
private readonly string foo = "bar";

创建一个名为“foo”的变量,其值为“bar”,并且该变量的值是只读的,这里是一个变量,与constant无关。 只读变量仍然是变量,它不能是常量。 变量和常量是互斥的。

也许这个问题有些过头了,但我仍然想听听别人的意见。 你怎么看?


5
“readonly”的命名不够好,应该被称为“immutable”。 - leppie
1
我同意书中使用这个术语并不是一个好选择。然而,这个问题并不适合在SO上讨论,因为它不符合“实际问题,有实际答案”的原则。过于闲聊和开放式的问题会降低我们网站的实用性,并将其他问题挤出首页。 - Eric J.
@JonSkeet:从技术上讲,在静态构造函数执行期间,该值是否可以更改? - Eric J.
readonly 表示它在运行时不能被赋值,因此使用了“运行时常量”这个术语。 - Alex
2
IL术语在这里相当不错:initonly - Marc Gravell
显示剩余3条评论
5个回答

10
我认为作者的意思是:
考虑以下例子:
public class A {

     public const int a = Compute();         

     private static int Compute(){

          /*some computation and return*/ 
          return some_computed_value;
     }
}

这段代码无法编译,因为你必须要有一个常量值才能将其赋给变量 a

因此,这就是“编译时常量”的意义。

如果你将代码改为以下形式,则可以编译:

public class A {

     public readonly int a = Compute();          

     private static int Compute(){
          /*some computation and return*/ 
          return some_computed_value;
     }
}

这个代码编译通过,它在运行时进行计算并将结果赋值给a。这就是运行时常量的含义。


但它不是一个常量,它是一个变量...只是...初始化后您无法更改其值。 - Cheng Chen
3
正如其他人指出的,我相信这本书所说的是常量(不可变)的值。在第一种情况下,在编译时它会变得不可改变,在第二种情况下,在运行时计算后才变得不可变。 - Tigran

6

正如你自己所注意到的那样,这个术语在语言规范中等是不被使用的。所以归咎于那本书吧! 我会称之为"只读字段",因为它就是一个"只读字段" - "只读"在这里的定义与初始化/构造函数有关,并且仅限于常规代码。例如,即使只读字段也是可以更改的...

// how to annoy your colleagues...
typeof(string).GetField("Empty").SetValue(null, " ");

(注意,这在最新的CLR版本上已经不再适用——JIT可能会将字段载入替换为ldstr——但它确实在很长一段时间内有效)
(更多关于在对象上执行此操作的“真正”原因与反序列化有关)

我从不称其为“****常量”,好吧...怪书吧! - Cheng Chen
3
@Danny 我说“归咎于书”时,我认为Jon的观点很好;也许这本书只是没有清楚地表明它何时使用描述性而不是正式术语。 - Marc Gravell
@Jacek 很不幸,这在最近的CLR中已经不再起作用了 - 我认为JIT检测到string.Empty并直接跳转到“ldstr”,而不是加载静态字段。 - Marc Gravell
你仍然可以更改实例的只读属性,但不能更改静态属性。 - Bitterblue
@mini-me 哦,你仍然可以更改静态只读字段。我要强调的问题是,在4.5(?)及以后版本中,JIT会完全重写string.Empty - 它永远不会实际查看该字段(除非使用反射,但反射不使用JIT)。 - Marc Gravell

2
一个只读变量只能在其构造函数中更改,并且可以用于复杂对象。常量变量不能在运行时更改,但只能用于像Int,Double,String这样的简单类型。运行时常量有点准确,但会混淆问题,常量和只读之间存在非常明显的差异,因此将一个名称类似于另一个可能不是一个好主意,尽管它们通常用于相同的目的。
这里有一个快速总结这些差异。

2
我会将readonly称为“只写变量”,这是由编译器而不是运行时检查的。您可以使用反射编写该字段,因此它在运行时不是常量。

一个只读变量不是在编译时检查,而是在运行时检查吗? - Ryan Amies
不,它可以在运行时编写。这可以通过序列化器、NHibernate等实现。 - Stefan Steinegger
@Marc:是啊...你也可以称之为“只在构造函数或字段初始化器变量中编写”。我的意思是,这变得很愚蠢了,不是吗? - Stefan Steinegger
3
我能够帮您进行翻译。建议使用IL术语:initonly;这也是cpp/CLR中使用的术语。在翻译时,我会尽力使句子更加通俗易懂,但不会改变原意,并且不包含解释或其他额外信息。 - Marc Gravell

2

constreadonly的区别

下面是constreadonly的完整比较:

|-----------------------------------------|-----------------------------------------|
|              Constant Field             |             Read-only Field             |
|-----------------------------------------|-----------------------------------------|
| Compile-time constant                   | Run-time constant                       |
|-----------------------------------------|-----------------------------------------|
| Assigned to an expression evaluated at  | Assigned to any valid expression at     |
| compile time                            | runtime                                 |
|-----------------------------------------|-----------------------------------------|
| Assigned on declaration                 | Assigned on declaration or constructor  |
|-----------------------------------------|-----------------------------------------|
| Only number, Boolean or string          | Any data type                           |
|-----------------------------------------|-----------------------------------------|
| Always static                           | Optionally static                       |
|-----------------------------------------|-----------------------------------------|

解释

  • 常量是静态的(无需实例化类即可访问)。
  • 常量只能在声明时赋值。
  • 常量是编译时常量,因为它们被分配给在编译时评估的表达式:当它们更改时,应重新编译项目以使用新值。
  • 只读字段是类中的变量,其保存初始化值(仅在构造函数或声明中),然后不再更改。
  • 只读字段是运行时常量,因为它们可以分配给任何有效表达式。
  • 只读字段适用于对象的实例,而不适用于该实例的属性。因此其他代码可以更改只读字段的实例属性。
  • 只读字段可以选择是静态的(如果您希望将它们共享给所有实例)。

何时使用const和何时使用readonly

  • 常量在它们是简单类型且它们的值永远不会更改时非常有用。
  • 只读字段在从源(文件、数据库或其他代码等)初始化,但是之后它们将不会更改时非常有用。

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