我读过有关const和static readonly字段的文章。我们有一些类只包含常量值。用于我们系统中的各种事物。所以我想知道我的观察是否正确:
对于所有公开的事物,这些常量值应该总是static readonly吗?只对内部/保护/私有值使用const?
你推荐什么?我是否应该甚至不使用static readonly字段,而是使用属性?
- 这里有一个非常有趣的例子,我刚刚发现有利于static readonly:尝试在IEnumerator中使用一个常量,它会触发一个不可恢复的yield,你会得到一个可怕的"内部编译器错误"。我没有在Unity3D之外测试代码,但我相信这是Mono或.NET错误。不过,这是一个C问题。
- const和readonly之间的区别可能是什么?
- 另一个区别是,可以在开关中使用常量字符串,但不能使用静态只读字符串
- static readonly不能在switch-case语句中使用,因为case变量,因此需要const变量。
- static readonly也不能用作属性参数。
public static readonly字段有点不寻常;public static属性(只有get属性)将更常见(可能由private static readonly字段支持)。
const值直接烧录到调用站点;这是双刃的:
- 如果在运行时提取该值(可能是从config获取),那么它是无用的。
- 如果更改常量的值,则需要重建所有客户端
- 但它可以更快,因为它避免了方法调用…
- …有时可能是由JIT内联的
如果该值永远不变,则常量为fine-Zero等;除此之外,static属性更常见。
- 为什么是一块地上的地产?如果这是一个不变的类,我看不出有什么不同。
- @Michael-和以前一样的原因;它隐藏了实现。您可能会发现(稍后)您需要惰性加载、基于配置、外观或其他。实际上,这两种情况都很好…
- 你所说的常数在运行时是什么意思?所以运行时不能读取从app.config或web.config中提取键值的常量?
- @根据定义,常量不是从配置文件中提取值;它在编译时作为文本烧掉。在运行时使用常量的唯一方法是通过对字段的反射。其他任何时候你试图使用它,编译器已经用你的常量代替了文字用法;也就是说,如果你的代码中的一个方法使用了6个常量,而你把它作为IL来检查,就不会提到任何常量查找;文字值只会在原位加载。
- @MarcGravell-警告:不能在switch/case语句中使用readonly字段,而需要它们是const字段。
- @卢西亚诺:你确定吗?我刚刚编译了一些代码,如class myclass public readonly string f;myclass f="a";switch(f)case"a"class myclass public readonly string f;myclass()f="a";switch(f)case"a":break;default:break;:break;default:break;
- @用户652688:是的。我敢肯定。尝试在case语句中使用只读变量。case语句需要的是常量值,而不是动态确定的值,例如只读变量,在代码执行期间,这些变量的值可能会发生更改(动态确定)。
- @user652688:用另一种方式解释:switch语句需要根据常量值(case语句)检查变量值。因此,在case语句中,只读变量不能用作常量。
- @卢西亚诺:我明白你的意思了,你可以打开只读变量,但是你不能用只读变量作为例子。这是有道理的,但一开始有点不清楚。
- @马克:对于常量,我看不到使用"public static int numberofwheels get return 4;"这样的属性实际上比使用"static readonly int numberofwheels=4"这样的公共字段更常见;您能详细说明您的想法吗,或者可能展示一个示例,说明您在这种情况下更经常看到什么?
- @MarcGravell不是所有属性的要点,因此您可以从一个字段开始,如果您以后需要添加功能,请在不破坏API的情况下更改属性?由于属性的使用类似于字段,因此更改是透明的。我认为这是方法转换器和访问器的整个属性点。
- @将字段更改为属性的didibus实际上会破坏API。C中的字段有效地充当变量,而C中的属性是编写getter方法和/或setter方法的语法助手。当涉及其他组件时,这一差异非常重要。如果将字段更改为属性,并且其他程序集依赖于此字段,则必须重新编译这些其他程序集。
- @stephenbooher已经有一段时间了,但我认为这就是@didibus所说的:当您使用属性设计API时,您可以在不破坏API的情况下更改属性访问的内容和方式(无论是常量、变量、其他函数、加载样式/数据库调用等),您将始终拥有该属性。我看不出在这方面,属性相对于赋值函数和访问函数有什么明显的优势,尽管…它们只是语法上的"简单"还是更"甜"?-也许我误解了你们两个(?)
- @为什么公共静态只读字段有点不寻常?
- @talalyousif所有的public字段都有点不寻常;大多数字段都是private字段,而且理由充分。当然,consts通常是public,但这是不同的。如果您经常暴露public字段,我建议这是一个错误。
如果消费者在不同的组件中,我将使用static readonly。把const和消费者放在两个不同的组件中是一个很好的方法,可以让你的脚受伤。
- 因此,我认为正如一些人所提到或暗示的,如果将常量公开,那么只对实际上是众所周知的常量的值使用const可能是明智的,否则应该将它们保留给内部、受保护或私有访问范围。
- @DIO它仍然存在的原因是它本身不是一个问题-它是需要注意的,但是跨装配边界内联常量的能力是一个很好的性能。真正需要理解的是,"常数"意味着"它永远不会改变"。
- @好吧,我不应该称之为"问题"。在我的工作中,我确实有const并在程序集之间共享它,但是我为每个部署或代码传送重新编译它。然而,这一事实确实值得注意。
- 因此,一般来说,internal const或public static readonly取决于所需的能见度。
- @在这些陈述中有两个截然不同的概念。可见性由public或internal和其他访问修饰符控制。第二个概念是static readonly与const,它控制字段是常量还是变量。您可以根据您的用例混合和匹配可见性和常量。
- @Michaelstum我的意思是,似乎你一般不应该将public与const一起使用,如果你需要public字段,最好使用static readonly。
- @是的,这不是一个糟糕的看待方式。有一些边缘情况需要考虑(例如,如果使用反射,或者属性上需要一个值),并且对public const有有效的使用(例如,标准的任何部分)。每当我使用XML时,都会有一个带有大量public const string的名称空间文件,但一般来说,public 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的范围内,那么存在从int到short的隐式转换就变得很重要了。1是)。请参见C语言规范中的隐式常量表达式转换。因此,必须考虑这两种过载。过载的Equals(short)是首选(任何short都是object,但并非所有object都是short。因此,y转换为short,并使用过载。然后,Equals比较两个值相同的short,得出True。
当y不是常数时,不存在从int到short的隐式转换。这是因为一般来说,一个int可能太大,不适合于一个short中。(确实存在一个明确的转换,但我没有说Equals((short)y),所以这是不相关的。)我们看到只应用了一个重载,Equals(object)重载。因此,y被装箱到object上。然后,Equals将System.Int16与System.Int32进行比较,由于运行时类型甚至不一致,这将产生False。
我们得出结论,在某些(罕见)情况下,将const类型的成员更改为static readonly字段(或其他可能的方式)可以更改程序的行为。
- 这是对公认答案的一个很好的补充。我想补充一点,数据类型的正确转换和其他类似的指导原则(如try catch等)应该是经验丰富的程序员的主要内容,而不是留给编译器。不过,我从这里学到了一些新东西。谢谢您。
- 哇,我用C语言编程已经很长时间了,我从来没有想到短的范围内的const int可以隐式转换为短的。我得说这很奇怪。我喜欢C,但是这些奇怪的前后矛盾似乎没有增加多少价值,但却增加了需要不断思考的脑力,这可能会让人恼火,特别是对于初学者。
- @Mikemarynowski说得对。但我认为,他们规定(除其他原因外)使声明short x = 42;合法。因为这里有一个int,即文字42,它含蓄地转化为short x。但是,他们可能会将这一点限制在数字文字上;但是,他们选择了允许像short x = y;这样的东西,其中y被定义为const int y = 42;,然后他们就这样结束了。
需要注意的一点是const仅限于基元/值类型(异常是字符串)
- 实际上,const也可以用于其他类型,只是必须将其初始化为空,这使得它无效:)
- 如System.Exception中的例外情况?:)
- @更准确地说,只有const可以使用的值类型是sbyte、byte、short、ushort、EDOCX11〔5〕、edcx11〔6〕、long、EDOCX11〔8〕、EDOCX11〔9〕、EDOCX11〔10〕、edcx11〔11〕、EDOCX11〔12〕12、edcx11〔12〕12、EDOCX11〔12〕12〔12〕12、EDOCX11〔10〔10〕10〔10〕10〔10〔10〕、EDOCX11〔7〕7〔7〔7〕、EDOCX11〔8〕、edcx11〔8〔8〕、edcx11〔9〕9〔9〕〔13〕加上任何enum型。const不能用于其他值类型,如DateTime或TimeSpan或BigInteger。它也不能用于IntPtr结构(某些人认为它是"原始"类型;C中的术语"原始类型"混淆)。???const可用于所有引用类型。如果类型为string,则可以指定任何字符串值。否则,该值必须为null。
- @Jeppestiginelsen——我最近和Servy讨论过这个问题——他指出你可以使用default制作任何东西(值和引用类型)const。对于struct类型,它是一个所有成员都设置为默认值的实例。
readonly关键字不同于const关键字。const字段只能在该字段的声明处初始化。readonly字段可以在声明或构造函数中初始化。因此,根据所使用的构造函数,readonly字段可以具有不同的值。此外,虽然const字段是编译时常量,但readonly字段可用于运行时常量。
此处提供简短清晰的msdn参考
静态只读:可以在运行时通过static构造函数更改值。但不是通过成员函数。
常数:默认为static。值不能从任何位置更改(ctor、函数、运行时等,不可从何处更改)。
只读:值可以在运行时通过构造函数更改。但不是通过成员函数。
你可以看看我的回购:c财产类型。
- 坏消息…断线!
- @找到你了:github.com/yeasin90/advanced csharp/blob/master/csharpadvanc‌&8203;ed/…
- 好片段暹罗????:)
const和readonly是相似的,但它们并不完全相同。
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重新编译X,Y仍将使用旧值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/
- 你能告诉我为什么你投反对票吗?这样我就可以更新我自己了。
- 不是dv,但可能这个答案并没有给已经全面的答案增加任何内容。
常量就像名称所暗示的,字段不会改变,通常在编译时在代码中静态定义。
只读变量是在特定条件下可以更改的字段。
当您第一次像常量一样声明它们时,可以对它们进行初始化,但通常在构造函数内的对象构造期间对它们进行初始化。
在上述条件下,在初始化之后不能更改它们。
静态只读对我来说似乎是一个糟糕的选择,因为如果它是静态的,并且永远不会改变,那么只需使用它public const,如果它可以改变,那么它就不是常量,然后根据需要,您可以使用只读变量,也可以只使用正则变量。
另外,另一个重要的区别是常量属于类,而只读变量属于实例!