【问题标题】:const vs. readonlyconst 与 readonly
【发布时间】:2012-07-04 06:36:09
【问题描述】:

今天我发现了一篇文章,其中const 字段被称为编译时常量,而readonly 字段被称为运行时常量。这两个短语来自《Effective C#》。我在 MSDN 和语言规范中进行了搜索,没有找到关于 运行时常量

没有冒犯,但我不认为 运行时常量 是一个合适的短语。

private readonly string foo = "bar";

创建一个名为“foo”的变量,其值为“bar”,值为只读,这里是变量,constant上没有业务。只读变量仍然是变量,它不能是常量。变量和常量是互斥的。

也许这个问题太过分了,我还是想听听别人的意见。你怎么看?

【问题讨论】:

  • readonly 是个坏名字。它应该被称为immutable
  • 我同意这不是书中术语的好选择。不过,这个问题并不适合 SO,因为practical, answerable questions based on actual problems that you face. Chatty, open-ended questions diminish the usefulness of our site and push other questions off the front page
  • 这是一个“常数”,因为它是一个不会也不能改变的值(反射除外)。它不是 C# 规范术语中的常量表达式,但我认为在书中描述它时这不是什么大问题。听起来你真的没有编程问题...
  • @JonSkeet:从技术上讲,值可以在静态构造函数的执行期间改变,不是吗?
  • 这里的 IL 术语非常好:initonly

标签: c# .net constants readonly


【解决方案1】:

我相信作者的意思是:

考虑例子:

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。 这是运行时常量

的意思

【讨论】:

  • 但它不是一个常数,它是一个变量……只是……初始化后不能改变它的值。
  • @DannyChen:正如其他人指出的那样,我相信这本书是在谈论恒定(不可改变)的价值。第一次是在编译时变成这样,在第二种情况下它变成在运行时(计算之后)。
【解决方案2】:

正如您自己所指出的,该术语未在语言规范等中使用。所以;怪那本书!我将其称为“只读字段”,因为它就是这样 - 这里的“只读”定义与初始化程序/构造函数相关,并且仅限于常规代码。例如,即使是只读字段也是可变的……

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

(请注意,这不再适用于最近的 CLR 版本 - JIT 可能会用 ldstr 替换字段加载 - 但它确实存在很长时间)

(更多真正对对象执行此操作的原因与反序列化有关)

【讨论】:

  • 我从不称它为“**** 常数”,好吧...怪这本书!
  • @Danny 当我说“责备这本书”时,我认为 Jon 的观点很好;当它使用 descriptive 而不是 formal 术语 时,也许根本就没有说清楚
  • @Jacek 不幸的是,这在最近的 CLR 中不再有效 - 我认为 JIT 检测到 string.Empty 并直接跳转到“ldstr”而不是加载静态字段
  • 您可以仍然更改实例只读。不过不是静态的。
  • @mini-me 哦,您仍然可以更改静态只读字段。我要强调的问题是,在 4.5 (?) 之后,JIT 完全重写了 string.Empty - 它永远不会实际上查看该字段(使用反射时除外,它不使用JIT)
【解决方案3】:

只读变量只能在其构造函数中更改,并且可以用于复杂对象。常量变量不能在运行时更改,但只能用于简单类型,如 Int、Double、String。运行时常量有点准确,但混淆了这个问题,常量和只读之间存在非常明显的区别,因此将一个命名为与另一个相似可能不是一个好主意,即使它们通常用于相同的目的。

快速总结差异here

【讨论】:

    【解决方案4】:

    我会调用 readonly 一个 “write once variable”,它由编译器检查,而不是在运行时检查。您可以使用反射编写该字段,因此它在运行时不是恒定的。

    【讨论】:

    • 只读变量不是在运行时检查,而不是在编译时检查吗?
    • 不,可以在运行时编写。这是由序列化程序、NHibernate 等完成的。
    • @Marc:是的......你也可以称之为“只写在构造函数或字段初始化变量”。我的意思是,它变得越来越愚蠢,不是吗?
    • @StefanSteinegger 我相信 IL 术语会很好用:initonly;这也是cpp/CLR中使用的术语
    【解决方案5】:

    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

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

    【讨论】:

      猜你喜欢
      • 2023-03-27
      • 2011-03-12
      • 2012-01-07
      • 1970-01-01
      • 2020-11-17
      • 2011-03-05
      • 1970-01-01
      • 2011-08-16
      • 2012-06-12
      相关资源
      最近更新 更多