'静态只读'vs.'cons'

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

对于所有公开的东西,这些常量值是否应该总是static readonly?并且只对internal/protected/private值使用const

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

448553 次浏览

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

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

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

如果值将从未更改,则const很好-Zero等做出合理的consts; p除此之外,static属性更常见。

需要注意的一点是const仅限于原始/值类型(例外是字符串)。

需要注意的一些更相关的事情:

const int a

  • 必须初始化。
  • 初始化必须在编译时间

readOnly int a

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

我的偏好是尽可能使用const,正如前面的答案中提到的,它仅限于文字表达式或不需要求值的东西。

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

如果消费者在不同的程序集中,我会使用static readonly。将const消费者放在两个不同的程序集中是射自己的脚的好方法。

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

这个简短而清晰的MSDN参考

const: const只不过是“常量”,一个变量的值是常量,但在编译时是常量。并且必须为其赋值。默认情况下,const是静态的,我们不能在整个程序中更改const变量的值。

静态只读:静态只读类型变量的值可以在运行时赋值,也可以在编译时赋值并在运行时更改。但是这个变量的值只能在静态构造函数中更改。不能再更改了。它在运行时只能更改一次

参考:c锐角

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

在某些情况下,const和非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

原因是方法#0有两个重载,一个接受shortSystem.Int16),一个接受objectSystem.Object)。现在的问题是,我的y参数是一个还是两个都适用。

y是编译时常量(文字)时,即const的情况,如果int是常量,并且C#编译器验证其值在short的范围内(42是),则确实存在隐式转换const7intconst8short变得很重要。参见C#语言规范中的const9。因此必须考虑两个重载。重载Equals(short)是首选(任何short都是object,但并非所有object都是short)。因此y被转换为short,并使用重载。然后const4比较两个值相同的short,这给出了const6。

y不是常量时,不存在从intshortint3转换。这是因为一般来说,int可能太大而无法放入short。(确实存在int4转换,但我没有说Equals((short)y),所以这不相关。)我们看到只有一个重载适用,Equals(object)。所以y被装箱到object。然后Equalsint0与int1进行比较,由于运行时类型甚至不一致,这将产生int2。

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

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

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

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

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

因为像3.14和0这样的值是编译时常量。然而,考虑一下你定义一个类型并希望提供它的一些预制实例的情况。例如,你可能想定义一个Color类并为黑色、白色等常见颜色提供“常量”。

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的客户端搞砸它,也许通过交换黑白值。不用说,这会引起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成员总是静态的,而只读成员可以是静态的,也可以不是,就像常规字段一样。

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

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

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

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

现在,生成的代码可以依赖于A. C是编译时常量的事实吗?也就是说,A. C的使用可以简单地替换为值0吗?如果你对此说“是”,那么这意味着A的开发人员不能改变A. C的初始化方式——这未经许可就束缚了A的开发人员的手脚。

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

const:

  1. 价值应在声明时给出
  2. 编译时间常数

只读:

  1. 值可以在声明时或在运行时使用构造器给出。值可能会因使用的构造函数而异。
  2. 运行时间常数

C#. Net中的const和静态只读字段之间存在细微差异

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

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

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

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

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

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

public const decimal ProgramVersion = 2.3;

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

另一种看待这个问题的方式是,任何可能的值根据定义,未来的变化不是恒定的,所以应该不代表一个。

静态只读

可以在运行时通过static构造函数更改该值。但不能通过成员函数。

恒定

默认情况下static。值不能从任何地方(构造函数、函数、运行时等)更改。

只读

可以在运行时通过构造函数更改该值。但不能通过成员函数。

您可以查看我的存储库:C#属性类型

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

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

它们可以在您第一次将它们声明为常量时初始化,但通常它们是在构造函数内的对象构造期间初始化的。

在上面提到的条件下,初始化发生后不能更改它们。

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

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

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

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

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

如果我们必须在不同的程序集中使用变量,静态只读将是更好的选择。请在下面的博客文章中查看完整的详细信息:

常量字符串-一个非常方便的方式来拍摄自己的脚

const(在编译时确定)可以在只读静态不能使用的情况下使用,比如在Switch语句或属性构造函数中。这是因为只读字段只在运行时解析,并且一些代码构造需要编译时保证。只读静态可以在构造函数中计算,这通常是一件必不可少和有用的事情。区别是功能性的,在我看来应该是它们的用法。

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

就我个人而言,我的默认值是只读静态的,因为它对我来说更具语义和逻辑意义,特别是因为大多数值在编译时不需要。而且,顺便说一句,公共只读静态数据并不像标记的答案所说的那样不寻常或不常见:例如,System.String.Empty是1。

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

静态字段属于对象的类型,而不是该类型的实例。因此,一旦类第一次被引用,静态字段将在剩余的时间里“生活”在内存中,该类型的所有实例都将引用静态字段的同一个实例。

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

如果释放内存对您更重要,则更喜欢使用const。如果速度快,则使用静态只读

有一个重要的问题,在上面的答案中没有提到,应该让你更喜欢“const”,特别是对于“int”、“string”等基本类型。

常量可以用作属性参数,静态只读字段不!

Azure函数HttpTrigger,在属性中不使用Http方法类

如果只有微软使用Http的GET、POST、DELETE等常量。

有可能写出

[HttpTrigger(AuthorizationLeve.Anonymous,  HttpMethods.Get)] // COMPILE ERROR: static readonly,

但我不得不求助于

[HttpTrigger(AuthorizationLeve.Anonymous,  "GET")] // STRING

或者使用我自己的常量:

public class HttpConstants{public const string Get = "GET";}
[HttpTrigger(AuthorizationLeve.Anonymous,  HttpConstants.Get)] // Compile FINE!

如果可以提供编译时常量,请使用const

private const int Total = 5;

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

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

这将导致编译错误,因为不可能在编译时获取值。

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

const

  1. 只能应用于字段。值应该在代码编译时。
  2. 适用于在编译代码之前已经知道的代码中删除魔法“字符串”、“int/Double”、(原始类型)等。
  3. 编译后,无论使用常量,值都将放置在编译后的代码中。因此,如果您在许多地方使用了一个巨大的字符串,那么在使其保持不变之前要小心。考虑使用静态只读。

静态只读

  1. 静态只读应用于字段/道具,静态可用于方法。(旁注)当静态应用于方法时,编译的代码不会将'this'参数传递给方法,因此您无法访问对象的实例数据。
  2. 适用于编译代码后可能会更改的值。例如从配置初始化的值、应用程序启动期间等。
  3. 编译代码后,在IL代码中使用ref to value,与使用const相比可能会慢一些,但编译后的代码很小

在重构期间,All const可以安全地转换为静态只读,但反之亦然,正如我们在上面看到的,当转换后的代码可能会中断,因为一些静态只读变量可以在构造函数中初始化。

另一个我不相信的区别是上面提到的:

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

static只获取属性确实会将CodeLens应用于它们。

代码镜头示例

我认为增加CodeLens是非常有价值的。

注意:当前使用Visual Studio 2022。

const、read只、静态readOnly——执行类似操作但有重要区别的关键字:

const-是一个值为常量并在编译时赋值的变量。您必须为其赋值。默认常量是静态的,我们不能在整个程序中更改const变量的值。

只读-表示我们可以在运行时更改的值,或者我们可以在运行时分配它,但只能通过非静态构造函数。

静态只读-值可以在运行时分配或在编译时分配并在运行时更改。但此变量的值只能在静态构造函数中更改。并且不能进一步更改。它只能在执行期间更改一次。

你可以在这里找到的例子-https://www.c-sharpcorner.com/UploadFile/c210df/difference-between-const-readonly-and-static-readonly-in-C-Sharp/