关于C#:静态只读vs常量

Static readonly vs const

我读过有关conststatic readonly字段的文章。我们有一些类只包含常量值。用于我们系统中的各种事物。所以我想知道我的观察是否正确:

对于所有公开的事物,这些常量值应该总是static readonly吗?只对内部/保护/私有值使用const

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


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

const值直接烧录到调用站点;这是双刃的:

  • 如果在运行时提取该值(可能是从config获取),那么它是无用的。
  • 如果更改常量的值,则需要重建所有客户端
  • 但它可以更快,因为它避免了方法调用…
  • …有时可能是由JIT内联的

如果该值永远不变,则常量为fine-Zero等;除此之外,static属性更常见。


如果消费者在不同的组件中,我将使用static readonly。把const和消费者放在两个不同的组件中是一个很好的方法,可以让你的脚受伤。


需要注意的更多相关事项:

cont int

  • 必须初始化。
  • 初始化必须在编译时进行。

只读int

  • 可以使用默认值,而不进行初始化。
  • 初始化可以在运行时完成(编辑:仅在构造函数内)。


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

在某些情况下,const和non-const具有不同的语义。例如:

1
2
3
4
5
6
7
const int y = 42;

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

打印出True,鉴于:

1
2
3
4
5
6
7
static readonly int y = 42;

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

False写道。

原因是方法x.Equals有两个重载,一个重载接收short(System.Int16),一个重载接收object(System.Object)。现在的问题是一个或两个都适用于我的y论点。

y是编译时常数(文字),即const的情况下,如果int是常数,并且C编译器验证其值在short的范围内,那么存在从intshort的隐式转换就变得很重要了。1是)。请参见C语言规范中的隐式常量表达式转换。因此,必须考虑这两种过载。过载的Equals(short)是首选(任何short都是object,但并非所有object都是short。因此,y转换为short,并使用过载。然后,Equals比较两个值相同的short,得出True

y不是常数时,不存在从intshort的隐式转换。这是因为一般来说,一个int可能太大,不适合于一个short中。(确实存在一个明确的转换,但我没有说Equals((short)y),所以这是不相关的。)我们看到只应用了一个重载,Equals(object)重载。因此,y被装箱到object上。然后,EqualsSystem.Int16System.Int32进行比较,由于运行时类型甚至不一致,这将产生False

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


需要注意的一点是const仅限于基元/值类型(异常是字符串)


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

此处提供简短清晰的msdn参考


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

常数:默认为static。值不能从任何位置更改(ctor、函数、运行时等,不可从何处更改)。

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

你可以看看我的回购:c财产类型。


constreadonly是相似的,但它们并不完全相同。

const字段是编译时常量,这意味着该值可以在编译时计算。readonly字段允许在构造类型期间运行某些代码的其他方案。施工后,不能更改readonly字段。

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

1
2
3
4
5
struct Test
{
    public const double Pi = 3.14;
    public const int Zero = 0;
}

因为像3.14和0这样的值是编译时常量。但是,考虑一下您定义类型并希望提供它的一些fab前实例的情况。例如,您可能希望定义一个颜色类,并为常见颜色(如黑色、白色等)提供"常量"。使用const成员是不可能的,因为右手边不是编译时常量。我们可以对常规静态成员执行此操作:

1
2
3
4
5
6
7
8
9
10
11
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);
}

但是没有什么可以阻止一个有颜色的客户去弄脏它,也许是通过交换黑白值。不用说,这会给颜色类的其他客户机带来恐慌。"只读"功能解决了这种情况。

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

1
2
3
4
5
6
7
8
9
10
11
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)使用了一个关键字,开发人员写道:

1
2
3
4
public class A
{
    public static const C = 0;
}

另一个开发人员编写的代码依赖于:

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

现在,生成的代码是否依赖于a.c是编译时常量这一事实?也就是说,交流电的使用可以简单地用0代替吗?如果您对此说"是",那么这意味着a的开发人员无法更改a.c的初始化方式——这将在未经许可的情况下将a的开发人员的双手绑在一起。

如果你对这个问题说"不",那么就会错过一个重要的优化。也许A的作者肯定A.C永远是零。使用const和readonly允许的开发人员指定意图。这有助于更好的版本控制行为和更好的性能。


我的首选是尽可能使用const,正如上面提到的,它仅限于文本表达式或不需要计算的内容。

如果我违背了这个限制,那么我将返回到静态只读状态,并提出一个警告。我通常使用一个公共静态属性,带有getter和一个支持私有静态只读字段,正如Marc在这里提到的。


Const: Const is nothing but"constant", a variable of which the value is constant but at compile time. And it's mandatory to assign a value to it. By default a const is static and we cannot change the value of a const variable throughout the entire program.

Static ReadOnly: A Static Readonly type variable's value can be assigned at runtime or assigned at compile time and changed at runtime. But this variable's value can only be changed in the static constructor. And cannot be changed further. It can change only once at runtime

参考:C-SharpCorner


静态只读字段在暴露于其他程序集的值可能在更高版本中更改。

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

1
public const decimal ProgramVersion = 2.3;

如果assembly Y引用X并使用此常量,则值2.3将在编译时烘焙成程序集Y。这意味着如果随后用常数设置为2.4重新编译XY仍将使用旧值2.3,直到重新编译Y。静态的只读字段避免了这个问题。

另一种看待这一点的方法是未来的变化并不是一成不变的,因此应该不能代表为一。


康斯特:

  • 申报时应给出价值
  • 编译时间常数
  • 只读:

  • 值可以通过声明或在运行时使用构造函数提供。该值可能会因使用的构造函数而有所不同。
  • 运行时间常数

  • 在C.NET中,常量字段和静态只读字段之间存在细微差别

    常量必须在编译时用值初始化。

    常量在默认情况下是静态的,需要用常量值初始化,以后不能修改。它不能与所有数据类型一起使用。对于ex-datetime。它不能与datetime数据类型一起使用。

    1
    2
    3
    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

    只读可以声明为静态,但不是必需的。声明时不需要初始化。它的值可以使用构造函数分配或更改一次。因此,有可能更改只读字段的值一次(不管是否为静态字段,这都不重要),这在const中是不可能的。


    const:const变量值必须与声明一起定义,然后定义不会更改。const是隐式静态的,因此如果不创建类实例,我们可以访问它们。这在编译时有一个值

    readonly:我们可以在声明时定义只读变量值,也可以在运行时使用构造函数。没有类实例,只读变量无法访问。

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

    如果必须使用不同程序集中的变量,静态只读将是更好的选择。请检查下面链接中的完整详细信息

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


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

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

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

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

    静态只读对我来说似乎是一个糟糕的选择,因为如果它是静态的,并且永远不会改变,那么只需使用它public const,如果它可以改变,那么它就不是常量,然后根据需要,您可以使用只读变量,也可以只使用正则变量。

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