静态只读 vs. 常量

1583

我了解了 const static readonly 字段。我们有一些仅包含常量值的类。它们在系统中用于各种目的。所以我想知道我的观察是否正确:

对于所有公共内容,这种类型的常量值是否应始终是 static readonly ?而仅对 internal / protected / private 值使用 const

您有什么建议?甚至我也不应该使用 static readonly 字段,而应该使用属性吗?


7
我找到了一个非常有趣的例子,支持使用 static readonly尝试在 IEnumerator 中使用 const 会触发无法到达的 yield,导致编译器出现可怕的“内部编译器错误”。虽然我没有在 Unity3D 之外测试过这段代码,但我相信这是 mono.NET问题。然而,这仍然是一个关于 c# 的问题。 - cregox
4
可能是 const 和 readonly 有什么区别? 的重复问题。 - nawfal
10
另一个不同之处在于,您可以在 switch 语句中使用 const 字符串,但不能使用 static readonly 字符串。 - flagg19
9
switch-case语句中,不能使用static readonly作为case变量,需要使用const来替代。翻译后的内容保持原意,通俗易懂,无解释,无额外内容返回。 - Mostafiz Rahman
4
static readonly 不能用作属性参数。 - Dread Boy
有关详细信息,请访问 https://enlear.academy/const-vs-readonly-vs-static-readonly-in-c-755c20aa0b57 - Trident
22个回答

11

当向其他程序集公开可能在以后版本中更改的值时,使用静态只读字段会更有优势。

例如,假设程序集 X 如下所示公开一个常量:

public const decimal ProgramVersion = 2.3;

如果汇编Y引用X并使用此常量,则当编译汇编Y时,值2.3将被嵌入到汇编Y中。这意味着,即使以后将X重新编译并将常量设置为2.4,Y仍将使用旧值2.3,直到重新编译Y。静态readonly字段可避免此问题。

另一种看待这个问题的方法是,任何可能在未来发生变化的值都不是常量的定义,并且因此不应表示为常量。


9

Const:Const就是“常量”,它是一个在编译时其值就确定了并且必须被赋值的变量。默认情况下,const是静态的,并且在整个程序中无法更改const变量的值。

Static ReadOnly:静态只读类型变量的值可以在运行时分配或者在编译时分配并在运行时更改。但是这个变量的值只能在静态构造函数中更改,并且不能再进行更改。它只能在运行时更改一次。

参考:c-sharpcorner


8

在 C# 中,const 和 static readonly 字段之间有一个小差别。

const 必须在编译时初始化值。

const 默认是静态的,需要使用常量值进行初始化,之后不能再修改。它不能与所有数据类型一起使用。例如 DateTime。无法与 DateTime 数据类型一起使用。

public const DateTime dt = DateTime.Today;  //throws compilation error
public const string Name = string.Empty;    //throws compilation error
public static readonly string Name = string.Empty; //No error, legal

readonly 可以被声明为静态的,但不是必需的。它不需要在声明时初始化。它的值可以通过构造函数进行赋值或更改。因此,readonly 字段的值可以被更改一次(无论是否为静态),而 const 无法更改。


5

const:

  1. 声明时必须给出数值
  2. 编译时常量

readonly:

  1. 声明时或在运行时使用构造函数时可以给出数值。数值可能因使用的构造函数而有所不同。
  2. 运行时常量

关于“应该给予”的问题:你是指“必须给予”吗?有没有其他的解决方法? - Peter Mortensen

2

如果你能提供编译时常量,请使用const

private const int Total = 5;

如果您需要在运行时评估值,请使用static readonly

private static readonly int GripKey = Animator.StringToHash("Grip");

这会导致编译错误,因为在编译时无法获取该值。
private const int GripKey = Animator.StringToHash("Grip");

2
声明conststatic readonly之间的另一个区别在于内存分配。
静态字段属于对象的类型而不是该类型的实例。因此,一旦引用了该类,静态字段将在其余时间内“存在”于内存中,并且该静态字段的相同实例将被该类型的所有实例引用。
另一方面,const字段“属于类型的实例”。
如果内存释放更重要,请使用const。如果速度更重要,则使用static readonly

2
一个 const (在编译时确定)可以在一些 readonly static 不能用的情况下使用,例如在 switch 语句或属性构造函数中。这是因为 readonly 字段仅在运行时解析,某些代码结构需要编译时保障。readonly static 可以在构造函数中计算,这通常是一个必要且有用的事情。区别在于它们的功能不同,应该根据我的意见来使用。
在内存分配方面,至少对于字符串(引用类型),似乎没有差异,两者都会被池化并引用到一个池化实例。
个人而言,我的默认选项是 readonly static,因为它在语义和逻辑上更有意义,特别是大多数值在编译时不需要。此外,公共的 readonly static 并不像标记的答案所述那样不寻常或罕见:例如,System.String.Empty 就是一个。

1

常量就像其名称所示,是不会改变的字段,通常在代码编译时静态定义。

只读变量是可以根据特定条件更改的字段。

它们可以在首次声明时像常量一样初始化,但通常在构造函数内部的对象构造期间进行初始化。

在上述条件下完成初始化后,它们将无法更改。

对我来说,静态只读听起来不太合适,因为如果它是静态的且永远不会改变,那么只需使用public const即可。如果它可以更改,则不是常量,然后根据您的需求,可以使用只读或普通变量。

另外,另一个重要的区别是常量属于类,而只读变量属于实例!


0

常量

  1. 只能应用于字段,值应该在代码编译时确定。
  2. 适用于消除代码中已知的“字符串”,“int/double”(原始类型)等魔术数字。
  3. 编译后,常量的值将放置在所有使用常量的编译代码中。因此,如果您有一个在多个地方使用的大型字符串,请注意在使其成为常量之前进行检查。可以考虑使用static read only。

静态只读

  1. 静态只读可应用于字段/属性,静态可以用于方法。(顺便提一句)当将静态应用于方法时,编译后的代码不会将“this”参数传递给方法,因此无法访问对象的实例数据。
  2. 适用于在编译代码后可能更改的值,例如从配置初始化的值,在应用程序启动期间等。
  3. 在编译代码后,将引用值的引用用于IL代码,与使用const相比可能较慢,但编译代码较小。

在重构期间,所有const都可以安全地转换为静态只读,但反之则不行,因为当转换代码时,某些静态只读变量可能在构造函数中初始化,导致代码出错。


我对C#中字符串常量的理解是,即使在多个地方使用,该常量仍然是内存中的单个字符串。因此,在const中的大型字符串仍然只是一个单一的值,在代码中的多个位置引用,如此答案所述:https://dev59.com/emct5IYBdhLWcg3wjuEj#12004049。这里使用的语言:“编译后,无论在哪里使用常量,该值都将放置在编译代码的各个位置。”听起来像它使用了更多的内存。 - Digital Coyote
是的,没错。const 值不是引用类型。它会在每个使用 const 的地方被替换。我在这里用一个小程序验证了它,并为您检查了汇编代码-https://github.com/santoshkaranam/constants - Santosh Karanam

0

还有一个区别,我认为上面没有提到:

conststatic readonly 值在 Visual Studio IDE 中不会应用 CodeLens

static 只读属性会应用 CodeLens。

Code Lens Example

我认为添加CodeLens非常有价值。

注意:目前使用的是Visual Studio 2022。


这似乎更适合于“属性 vs. 字段”线程,而不是当前问题“Static readonly vs. const”。另外,属性还有其他优点;您可以稍后在get访问器下的逻辑更复杂,而不会改变成员使用者的“契约”。 - Jeppe Stig Nielsen

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