我已经了解了常量和静态只读字段。我们有一些类只包含常量值。它们用于我们系统中的各种事情。所以我想知道我的观察是否正确:

对于所有公开的内容,这些常量值是否总是静态只读的?并且只对内部/受保护/私有值使用const?

你有什么建议?我甚至应该不使用静态只读字段,而应该使用财产吗?


当前回答

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

在内存分配方面,至少对于字符串(作为引用类型)来说,似乎没有区别,因为两者都是内部的,并且将引用一个内部的实例。

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

其他回答

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

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

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有两个重载,一个接受短(System.Int16),另一个接受对象(System.object)。现在的问题是,是一个还是两个都适用于y参数。

当y是编译时常数(文字)(常量)时,如果int是常量,并且C#编译器验证其值是否在short(42是)的范围内,那么从int到short的隐式转换就变得很重要了。请参阅C#语言规范中的隐式常量表达式转换。因此,必须考虑这两种过载。首选重载Equals(short)(任何short都是一个对象,但不是所有对象都是短的)。所以y被转换为short,并使用过载。然后Equals比较两个短的相同值,结果为真。

当y不是常数时,不存在从int到short的隐式转换。这是因为一般来说,int可能太大,无法放入short。(确实存在显式转换,但我没有说Equals((短)y),所以这不相关。)我们看到只有一个重载适用,Equals(对象)重载。所以y被装箱为对象。然后Equals将比较System.Int16和System.Int32,因为运行时类型甚至不一致,这将产生false。

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

我的偏好是尽可能使用const,正如前面的回答中所提到的,它仅限于字面表达或不需要求值的内容。

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

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

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

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

只读可以声明为静态,但不是必需的。无需在声明时进行初始化。它的值可以使用构造函数赋值或更改一次。所以,有可能更改只读字段的值一次(不管它是静态的还是非静态的),这在const中是不可能的。

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

从这个简短而清晰的MSDN参考中。

需要注意的几个相关事项:

常量int a

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

只读int a

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