静态只读与常量


1386

我已阅读有关conststatic readonly领域。我们有一些仅包含常量值的类。用于我们系统中的各种事物。所以我想知道我的观察是否正确:

这些常量值是否应该始终static readonly适用于所有公开的东西?并且仅const用于内部/受保护/私有值吗?

您有什么推荐的吗?我是否应该甚至不使用static readonly字段,而应该使用属性?


5
这是我刚刚发现的一个非常有趣的单例案例static readonly尝试在内部使用const,IEnumerator这将触发无法确定的操作, yield并且您会收到可怕的“内部编译器错误”。我没有在Unity3D之外测试代码,但是我相信这是mono.NET bug。但是,这是一个C#问题。
cregox


8
另一个区别是您可以在开关中使用const字符串,但不能使用静态只读字符串
flagg19

7
static readonly不能在switch-case语句中用作case变量,因此const是必需的。
Mostafiz Rahman'5

3
static readonly也不能用作属性参数
恐惧男孩

Answers:


940

public static readonly字段有点不寻常;public static属性(只有get)会更常见(可能由private static readonly字段支持)。

const值直接烧入呼叫站点;这是双刃的:

  • 如果在运行时获取值(可能是从config中获取)是没有用的
  • 如果更改const的值,则需要重建所有客户端
  • 但它可以更快,因为它避免了方法调用...
  • ... JIT有时可能内联了

如果该值永远不变,则const很好- Zero等使之成为合理的consts; p除此之外,static属性更常见。


13
为什么在田地上拥有财产?如果这是一门不变的课,我没什么区别。
Michael Hedgpeth,2009年

73
@Michael-一如既往的理由;它隐藏了实现。您可能会发现(后来)需要延迟加载,基于配置,外观或其他任何内容。实际上,这两种方法通常都很好...
Marc Gravell

42
@CoffeeAddict根据定义,常量不是从配置文件中提取值;它在编译时作为文字被烧掉。您可以在运行时使用常量的唯一方法是通过对字段的反射。在您尝试使用它的任何其他时间,编译器已经将您的常量用法替换为文字用法。例如,如果代码中的方法使用6个常量,并且将其作为IL进行检查,则不会提及任何常量查找;文字值将
仅就

37
@MarcGravell-注意:readonly字段不能在switch / case语句中使用,相反,您需要将它们设为const
卢西亚诺

7
@didibus实际上,将字段更改为属性确实会破坏API。C#中的字段实际上就像变量一样,而C#中的属性是用于编写getter方法和/或setter方法的语法助手。当涉及其他装配时,这一区别很重要。如果将字段更改为属性,并且其他程序集依赖于该字段,则必须重新编译这些其他程序集。
Stephen Booher 2013年

237

static readonly如果消费者在不同的程序集中,我会使用。让使用者const使用者处于两个不同的组合中,这是一个让自己步履蹒跚的好方法。


5
因此,我认为,正如某些人提到或暗示的,明智的是,仅将const用于实际上是众所周知的常量的值(如果将其公开),否则应保留它们以供内部,受保护或私有访问范围使用。
jpierson 2011年

1
@Dio之所以仍然存在,是因为它本身并不是问题-需要注意的是,但是跨程序集内联const的能力对于性能而言是一件好事。真正理解“恒定”表示“它永远不会改变”只是一个问题。
Michael Stum

1
@MichaelStum好吧,我不应该将其称为“问题”。在我的工作中,我确实拥有const并在程序集中共享它,但是我会针对每次部署或代码交付重新编译。尽管如此,这一事实绝对值得一提。
Dio Phung 2014年

1
因此,通常internal const还是public static readonly取决于所需的可见性。
Iiridayn

2
@Iiridayn是的,那不是不好的观察方法。需要考虑一些极端情况(例如,如果使用Reflection或属性上需要一个值),并且有一些有效的用法public const(例如,标准的任何部分。当我使用XML时,带有一堆public const string。的名称空间文件。)但是,一般而言,public const仅应在正确考虑其含义后使用。
Michael Stum


176

这只是对其他答案的补充。我不再重复(现在是四年后)。

在某些情况下,a const和非常量具有不同的语义。例如:

const int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

打印出True,而:

static readonly int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

写道False

原因是该方法x.Equals有两个重载,一个重载shortSystem.Int16),一个重载objectSystem.Object)。现在的问题是我的y论点是否适用于其中之一。

y是一个编译时间常数(字面),所述const情况下,也有不存在的隐式转换变得重要 int short提供的int是一个常数,并且条件是C#编译器验证其值的范围内short(这42是)。请参见C#语言规范中的隐式常量表达式转换。因此,必须考虑两个重载。过载Equals(short)是优选的(任何short是一个object,但不是全部objectshort)。因此y将转换为short,并使用重载。然后Equals比较两个short相同的值,得出true

y不是常数时,不存在从到的隐式转换。那是因为一般而言,可能太大而无法放入。(确实存在显式转换,但我没有说,所以不相关。)我们看到只有一个重载适用。如此装箱。然后将a 与a 进行比较,并且由于运行时类型甚至不一致,因此会产生。intshortintshortEquals((short)y)Equals(object)yobjectEqualsSystem.Int16System.Int32false

我们得出结论,在某些(罕见)情况下,将const类型成员更改为static readonly字段(或在可能的情况下,以其他方式)可以更改程序的行为。


17
对已接受答案的很好补充。我想补充一点,数据类型和其他类似准则(例如try catch等)的正确转换应该是经验丰富的程序员的必备内容,而不是交给编译器。不过,我从这里学到了一些新东西。谢谢。
Uknight

哇,我已经用C#编程很长时间了,我从来没有想过short范围内的const int可以隐式转换为short。我必须说这很奇怪。我喜欢C#,但是这些奇怪的矛盾似乎并没有增加太多的价值,但却增加了很多需要不断思考的脑力,这会令人讨厌,特别是对于初学者。
Mike Marynowski

@MikeMarynowski足够正确。但是我认为他们制定了该规则(除其他原因外)以使声明short x = 42;合法。因为那里有一个int,即文字42,它隐式地变成了short x。但是后来,他们可能只将其限制为数字文字。但是,他们选择也允许将short x = y;where y定义为const int y = 42;,然后最终结束了。
Jeppe Stig Nielsen

87

需要注意的一件事是const仅限于基本类型/值类型(字符串除外)


30
实际上const也可以用于其他类型,除了必须将其初始化为null之外,这使其变得无用:)
nawfal

6
异常如System.Exception?:)
Memet Olsen 2014年

4
@nawfal更确切地说,仅值类型为其中const可以使用,是sbytebyteshortushortintuintlongulongcharfloatdoubledecimalbool,加上任何enum类型。const不能用于其它类型的值,如DateTimeTimeSpanBigInteger。它也不能用于该IntPtr结构(某些人认为是“原始”类型;在C#中术语“原始类型”令人困惑)。↵↵ const可以用于所有参考类型。如果类型为string,则可以指定任何字符串值。否则,该值必须为null
杰普·斯蒂格·尼尔森

@JeppeStigNielsen -我最近有一个说法servy这一点-他指出,你可以做任何事情(值类型和引用类型)const使用default。对于struct类型,它是一个实例,其所有成员均设置为默认值。
李慧夏

28

静态只读:可以static在运行时通过构造函数更改值。但不是通过成员函数。

常量:默认情况下static。不能在任何地方(Ctor,Function,运行时等,无处)更改值。

只读:可以在运行时通过构造函数更改值。但不是通过成员函数。

您可以看一下我的仓库:C#属性类型


1
坏消息...链接断开!
R


暹罗(Siamভাই)的摘要:)
穆罕默德·阿什库扎曼

25

所述readonly关键字是从不同的const关键字。一个const字段只能在外地的声明进行初始化。一个readonly字段可以在声明或构造函数初始化。因此,readonly根据所使用的构造函数,字段可以具有不同的值。另外,虽然const字段是编译时常量,但该readonly字段可用于运行时常量

简短明了的MSDN参考资料在这里


16

constreadonly相似,但并不完全相同。

const字段是一个编译时间常数,这意味着该值可以在编译时计算。甲readonly字段允许在其中一些代码必须结构的类型的期间运行附加情景。构造后,readonly不能更改字段。

例如,const成员可用于定义成员,例如:

struct Test
{
    public const double Pi = 3.14;
    public const int Zero = 0;
}

由于像3.14和0这样的值是编译时常量。但是,请考虑定义一种类型并想提供一些预制实例的情况。例如,您可能想要定义一个Color类,并为常见的颜色(如黑色,白色等)提供“常量”。使用const成员是不可能的,因为右侧不是编译时常量。可以使用常规静态成员执行此操作:

public class Color
{
    public static Color Black = new Color(0, 0, 0);
    public static Color White = new Color(255, 255, 255);
    public static Color Red   = new Color(255, 0, 0);
    public static Color Green = new Color(0, 255, 0);
    public static Color Blue  = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}

但是,没有什么可以阻止Color的客户使用它了,也许可以通过交换Black和White值来解决。不用说,这会使Color类的其他客户端感到震惊。“只读”功能解决了这种情况。

通过readonly在声明中简单地引入关键字,我们保留了灵活的初始化,同时防止了客户端代码的混乱。

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red   = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue  = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}

有趣的是,const成员始终是静态的,而readonly成员可以是静态的,也可以不是静态的,就像常规字段一样。

可以将单个关键字用于这两个目的,但这会导致版本问题或性能问题。假设一会儿,我们为此关键字(const)使用了一个关键字,并且开发人员写道:

public class A
{
    public static const C = 0;
}

另一位开发人员编写了依赖于A的代码:

public class B
{
    static void Main() => Console.WriteLine(A.C);
}

现在,生成的代码是否可以依赖AC是编译时常量的事实?即,AC的使用是否可以简单地替换为值0?如果对此说“是”,则意味着A的开发人员无法更改AC初始化的方式-未经许可,这会束缚A的开发人员的双手。

如果您对这个问题说“否”,那么将错过重要的优化。也许A的作者肯定AC将始终为零。const和readonly的使用允许A的开发人员指定意图。这样可以实现更好的版本控制行为以及更好的性能。


12

我的首选是尽可能使用const,如上所述,它仅限于文字表达式或不需要求值的内容。

如果我遇到了这个限制,那么我会退一步回到静态readonly,但要注意一点。正如Marc 在这里提到的,我通常会使用带有getter和后备私有static readonly字段的公共静态属性。


7

常量:常量就是“常量”,它的值是恒定的,但在编译时才是常量。并且必须为其分配值。默认情况下,const是静态的,我们无法在整个程序中更改const变量的值。

静态只读:静态只读类型变量的值可以在运行时分配,也可以在编译时分配,并在运行时更改。但是只能在静态构造函数中更改此变量的值。并且无法进一步更改。在运行时只能更改一次

参考:c-sharpcorner


6

当向其他程序集显示在更高版本中可能会更改的值时,静态只读字段会很有用。

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

public const decimal ProgramVersion = 2.3;

如果程序集Y引用X并使用此常量,则Y编译时将值2.3烘焙到程序集中。这意味着,如果X以后将常量设置为2.4进行重新编译,Y则仍将使用旧值2.3,直到Y重新编译为止。静态只读字段可避免此问题。

另一种看待这种情况的方式是,将来可能发生变化的任何值在定义上都不是恒定的,因此不应表示为一个值。


3

const:

  1. 价值应在申报时给予
  2. 编译时间常数

只读:

  1. 可以在声明时或在运行时使用构造函数来提供该值。该值可能会因所使用的构造函数而异。
  2. 运行时间常数

3

const:const变量值必须与声明一起定义,此后它将保持不变。const是隐式静态的,因此无需创建类实例就可以访问它们。这在编译时很有价值

ReadOnly:我们可以在声明时定义的只读变量值,以及在运行时使用构造函数。只读变量不能在没有类实例的情况下访问。

静态只读:我们可以在声明时定义静态静态变量值,并且只能通过静态构造函数而不是任何其他构造函数来定义这些变量。我们也可以在不创建类实例的情况下访问这些变量(作为静态变量)。

如果我们必须在不同的程序集中使用变量,则静态只读将是更好的选择。请在下面的链接中查看全部详细信息

https://www.stum.de/2009/01/14/const-strings-a-very-convenient-way-to-shoot-yourself-in-the-foot/


你能告诉我你为什么对答案不满意吗,所以我也可以在这里更新自己。
user1756922

不是DV,但可能是此答案并未真正为此处已经全面的答案添加任何内容。
Marc L.

确实,记得在90年代后期的Java中,我们在一个项目中有多个人生产带有互操作(彼此引用)的类文件的不同jar,并且公共const字符串存在版本控制问题,因为它们被复制
George Birbilis

2

C#.Net中的const和静态只读字段之间有微小的区别

const必须在编译时用value初始化。

const默认情况下是静态的,需要使用常量值进行初始化,以后将无法对其进行修改。并非所有数据类型都可以使用它。对于exDateTime。不能与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则不可能。


0

常量就像名称中所暗示的那样,字段不变,通常在代码编译时静态定义。

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

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

在上述条件下,初始化后不能更改它们。

静态只读听起来对我来说是一个糟糕的选择,因为如果它是静态的并且永远不会更改,那么只需使用它的public const,如果它可以更改则不是一个常数,然后根据您的需要,可以使用read -仅或仅是常规变量。

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


0

const(在编译时确定)可以在只读静态变量不能使用的情况下使用,例如switch语句或属性构造函数中。这是因为只读字段仅在运行时解析,并且某些代码构造需要编译时保证。可以在构造函数中计算只读静态变量,这通常是必要且有用的事情。区别在于功能,我认为它们的用法也应如此。

就内存分配而言,至少对于字符串(作为引用类型)而言,似乎没有区别,因为两者都是被嵌入的,并且将引用一个被嵌入的实例。

就我个人而言,我的默认值是静态只读,因为它对我来说更具语义和逻辑意义,尤其是因为在编译时不需要大多数值。而且,顺便说一句,公开的只读静态数据并不罕见或不稀奇,如标记的答案所述:例如,System.String.Empty是一个。


0

声明const静态只读之间的另一个区别是内存分配。

静态字段属于对象的类型,而不是该类型的实例。结果,一旦首次引用该类,静态字段将在其余时间“保留”在内存中,并且该类型的所有实例都将引用该静态字段的相同实例。

另一方面,const字段“属于类型的实例。

如果对您而言,释放内存更重要,请使用const。如果速度快,则使用静态readonly

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.