C#中const和readonly有什么区别?

1692

constreadonly 在 C# 中有什么区别?

在什么情况下会使用其中一个而不是另一个?


4
我需要查看好几个答案才找到这个链接,但它很不错。Eric Lippert在C#中关于不可变性的看法。 - Frank Bryce
5
@donstack,根据C# 参考文档的说明,只读字段可以在字段声明和构造函数中被赋值和重新赋值多次。 - Marques
这里是const和readonly的详细解释: https://youtu.be/jA30qZNGNoM - Parveen
30个回答

1544

除了明显的区别外

  • const 必须在定义时声明值,而 readonly 值可以动态计算,但需要在构造函数退出之前分配。之后它就会被冻结。
  • const 是隐式的 static。您可以使用 ClassName.ConstantName 表示法来访问它们。

这有一个细微的差别。考虑在 AssemblyA 中定义的类。

public class Const_V_Readonly
{
  public const int I_CONST_VALUE = 2;
  public readonly int I_RO_VALUE;
  public Const_V_Readonly()
  {
     I_RO_VALUE = 3;
  }
}
AssemblyB引用了AssemblyA并在代码中使用这些值。当编译时:
  • 对于const值,它就像查找和替换一样。值2被“固化”在AssemblyB的IL中。这意味着如果明天我将I_CONST_VALUE更新为20,则AssemblyB仍然具有2,直到重新编译它
  • 对于readonly值,它就像是对内存位置的ref。该值不会固化在AssemblyB的IL中。这意味着如果更改了内存位置,AssemblyB会获得新值而无需重新编译。因此,如果将I_RO_VALUE更新为30,则只需要构建AssemblyA,所有客户端都不需要重新编译。
因此,如果您确定常量的值不会改变,请使用const
public const int CM_IN_A_METER = 100;

但如果你有一个可能会改变的常量(例如与精度相关),或者不确定时,请使用readonly

public readonly float PI = 3.14;

更新:Aku需要得到提及,因为他首先指出了这一点。此外,我需要推荐我学习这个的地方:Effective C# - Bill Wagner


118
“static”这个点似乎是最重要和最有用的点 - “常量被隐式地视为静态”。 - LCJ
42
关于参考值部分是最重要的。常量值可以被优化掉。 - CodingBarfield
32
“readonly”变量可以在构造函数外部进行更改(反射)。只有编译器试图阻止您在构造函数外修改变量。 - Bitterblue
14
readonly 变量是指一旦构造函数完成后就不允许再被改变,即使通过反射也不行。虽然运行时没有强制执行这个规则,但是也不应该认为这样就可以修改 string.Empty"Hello, world!",或者认为代码不需要假定 string.Empty 总是一个空字符串。 - user743382
12
该文章探讨了只读字段的开销,非常值得一读。 - CAD bloke
显示剩余6条评论

327

常量有一个陷阱!如果你从另一个程序集中引用一个常量,它的值将被编译到调用程序集中。这样当你在被引用的程序集中更新常量时,它在调用程序集中不会改变!


17
在反编译(Reflector、ILSpy等工具)过程中,无论是在同一个程序集还是在另一个程序集中,常量“NEVER EVER”都不会被任何人引用,因此你无法分析编译代码中常量的使用情况。 - springy76

200

常量

  • 默认情况下,常量是静态的
  • 它们必须在编译时具有值(例如,您可以使用3.14 * 2,但不能调用方法)
  • 可以在函数内声明
  • 它们被复制到使用它们的每个程序集中(每个程序集都会获得值的本地副本)
  • 可以在属性中使用

只读实例字段

  • 必须在构造函数退出时设置值
  • 在创建实例时进行评估

静态只读字段

  • 当代码执行命中类引用时进行评估(创建新实例或执行静态方法时)
  • 必须在静态构造函数完成时具有已评估的值
  • 不建议在这些字段上放置ThreadStaticAttribute(静态构造函数仅在一个线程中执行,并为其线程设置值;所有其他线程将具有未初始化的值)

75
只是补充一下,对于引用类型来说,readonly 只会使得引用本身变为只读,而不是其中的值。例如:
public class Const_V_Readonly
{
  public const int I_CONST_VALUE = 2;
  public readonly char[] I_RO_VALUE = new Char[]{'a', 'b', 'c'};

  public UpdateReadonly()
  {
     I_RO_VALUE[0] = 'V'; //perfectly legal and will update the value
     I_RO_VALUE = new char[]{'V'}; //will cause compiler error
  }
}

除了 string 这种类型,您还可以使用哪些引用类型作为常量? - springy76
除了字符串以外,你可以使用“const”关键字来定义其他引用类型的常量,但是这些常量只能赋值为“null”。 - Mike Rosoft
@user1333 你把解引用的值和被引用的值弄混了。在你的示例中,只读的值是“Char[]”。在第一次更新语句中更改的值是通过引用访问的内容,而不是引用本身。 - Suncat2000

51

const:无法在任何地方更改。

readonly:此值只能在构造函数中更改。不能在普通函数中更改。


49

这里有解释。总结:常量必须在声明时进行初始化,只读字段可以在构造函数中初始化(因此根据使用的构造函数而有不同的值)。

编辑:请参阅Gishu的陷阱以了解微妙差别。


很遗憾,答案中的链接已经失效了 - DNS_PROBE_FINISHED_NXDOMAIN - Pang
@Pang,答案中的链接可以在Wayback Machine上找到 - 例如2012年的存档版本 - Jaroslav Svestka

41

常量成员在编译时定义并且无法在运行时更改。使用const关键字将常量声明为字段,并且必须在声明时进行初始化。

public class MyClass
{
    public const double PI1 = 3.14159;
}

readonly成员变量类似于常量,它代表一个不可变的值。不同之处在于,readonly成员变量可以在运行时,在构造函数中初始化,并且也可以像声明时一样被初始化。

public class MyClass1
{
     public readonly double PI2 = 3.14159;

     //or

     public readonly double PI3;

     public MyClass2()
     {
         PI3 = 3.14159;
     }
}

const

  • 常量不能被声明为 static (它们会隐式地成为静态的)
  • 常量的值在编译时计算
  • 常量只能在声明时初始化

readonly

  • readonly 可以是实例级别或静态的
  • 值在运行时计算
  • readonly 可以在声明时进行初始化,也可以在构造函数中通过代码进行初始化

6
“它们不能是静态的”,它们确实是静态的。如果您指的是“无法声明static const int i = 0;”,请明确说明。 - nawfal
你能解释一下为什么不能在方法内部声明 const 吗? - Minh Tran

37

readonly有一个小问题。在构造函数中,可以多次设置只读字段的值。即使这个值在两个不同的链接构造函数中设置,它仍然是被允许的。

public class Sample {
    private readonly string ro;

    public Sample() {
        ro = "set";
    }

    public Sample(string value) : this() {
        ro = value; // this works even though it was set in the no-arg ctor
    }
}

34

const是编译时常量,而readonly允许在运行时计算一个值并在构造函数或字段初始化器中设置。因此,'const'始终保持不变,但'readonly'一旦被赋值就是只读的。

C#团队的Eric Lippert在这里提供了有关不同类型的不可变性的更多信息。


22

这里有另一个链接展示了const在版本安全性方面不具备优势,也与引用类型无关。

总结:

  • const属性的值在编译时被设置并且在运行时不能更改
  • 无法将const标记为static,该关键字表示它们是静态字段,而readonly字段可以这样做
  • 除了值(原始)类型之外,const不能是任何其他类型
  • readonly关键字将字段标记为不可更改。但是,该属性可以在类的构造函数内更改
  • readonly only也可以与static组合使用,使其表现出与const相同的方式(至少表面上如此)。如果查看IL,则两者之间存在显着差异
  • const字段在IL中标记为“literal”,而readonly则是“initonly”

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