const
和readonly
有什么区别? 什么时候可以使用另一个?
#1 楼
除了在定义const
VS时必须声明值的明显差异外,可以动态计算readonly
值,但需要在构造函数退出之前进行赋值。之后将其冻结。的const隐式为
static
。您可以使用ClassName.ConstantName
表示法来访问它们。有细微的差别。考虑在
AssemblyA
中定义的类。public class Const_V_Readonly
{
public const int I_CONST_VALUE = 2;
public readonly int I_RO_VALUE;
public Const_V_Readonly()
{
I_RO_VALUE = 3;
}
}
AssemblyB
引用AssemblyA
并在代码中使用这些值。编译后,对于const
值,它就像一个查找替换,值2被“烘焙”到AssemblyB
的IL中。这意味着,如果明天我将在将来将I_CONST_VALUE
更新为20。在重新编译之前,AssemblyB
仍将有2个。对于
readonly
值,它就像一个ref
到一个内存位置。该值未烘焙到AssemblyB
的IL中。这意味着,如果内存位置已更新,则AssemblyB
将获得新值而无需重新编译。因此,如果将I_RO_VALUE
更新为30,则只需构建AssemblyA
。 所以不需要重新编译所有客户端。因此,如果您确信常量的值不会改变,请使用
const
。 public const int CM_IN_A_METER = 100;
但是如果您有一个可能会改变的常数(例如w.r.t.精度),或者有疑问,请使用
readonly
。 public readonly float PI = 3.14;
更新:Aku需要得到提及,因为他首先指出了这一点。另外,我需要在我学到的知识上加插。.有效的C#-Bill Wagner
评论
静态点似乎是最重要和最有用的点-const是隐式静态的
– LCJ
2013年1月21日14:00
关于参考值的部分是最重要的部分。常量值可以优化掉。
–CodingBarfield
2013年6月5日7:03
只读变量可以在构造函数外部更改(反射)。只是编译器试图阻止您在构造函数之外修改var。
– Bitterblue
2013年6月5日14:10
构造函数完成后,即使通过反射,也不允许更改@ mini-me只读变量。运行时碰巧不执行此操作。运行时也不会强制您不要更改string.Empty到“ Hello,world!”,但是我仍然不会声称这使string.Empty可修改,或者该代码不应假定该string.Empty。始终为零长度的字符串。
–user743382
14年4月14日在11:55
blogs.msmvps.com/jonskeet/2014/07/16/…是一个有趣的只读只读开销
– CAD bloke
2014年8月5日在21:52
#2 楼
有一个const的陷阱!如果从另一个程序集中引用一个常量,则其值将直接编译到调用程序集中。这样,当您在引用的程序集中更新常量时,在调用的程序集中将不会更改该常量!评论
在反编译(Reflector,ILSpy,..)时,无论是同一程序集还是另一程序集,任何人都不能引用一个常量,因此您根本无法分析常量在已编译代码中的使用。
– springy76
16年8月19日在13:02
#3 楼
常量默认情况下常量是静态的
它们在编译时必须具有一个值(例如,您可以拥有3.14 * 2,但不能调用方法)
在函数中声明
复制到使用它们的每个程序集中(每个程序集都获取值的本地副本)
可以在属性中使用
只读实例字段
在构造函数退出时必须具有设置值
创建实例时进行评估
静态只读字段
>在代码执行达到类引用时(在创建新实例或执行静态方法时)进行评估
在静态构造函数完成时必须具有评估值
不建议将ThreadStaticAttribute放在这些(静态构造函数将仅在一个线程中执行,并将为其线程设置值;所有其他线程将具有未初始化的值)
评论
通过未初始化,您是说其他线程会看到该类型的默认值还是会看到垃圾值?
–西蒙·贝伯
10月9日6:09
#4 楼
只需添加,引用类型的readonly
仅使引用只读而不是值。例如:public class Const_V_Readonly
{
public const int I_CONST_VALUE = 2;
public readonly char[] I_RO_VALUE = new Char[]{'a', 'b', 'c'};
public UpdateReadonly()
{
I_RO_VALUE[0] = 'V'; //perfectly legal and will update the value
I_RO_VALUE = new char[]{'V'}; //will cause compiler error
}
}
评论
除了字符串,还有其他引用类型可以用作常量吗?
– springy76
16年8月19日在13:02
您可以将const与字符串以外的其他引用类型一起使用,但该常量只能具有null值。
– Mike Rosoft
18年4月27日在14:28
@ user1333您正在将取消引用的值与引用的值混淆。您的示例中的实例只读值是Char []。您在第一条update语句中更改的值是通过引用而不是引用本身访问的内容。
– Suncat2000
9月22日12:46
#5 楼
这解释了它。摘要:const必须在声明时初始化,只读可以在构造函数上初始化(因此,根据所使用的构造函数的不同,它的值也不同)。编辑:有关细微的差异,请参见上面的Gishu的陷阱
#6 楼
const
:不能在任何地方更改。readonly
:该值只能在构造函数中更改。无法在常规功能中更改。#7 楼
常量成员是在编译时定义的,不能在运行时更改。使用const
关键字将常量声明为字段,并且必须在声明它们时对其进行初始化。public class MyClass
{
public const double PI1 = 3.14159;
}
readonly
成员就像常量一样,它表示不变值。区别在于,readonly
成员可以在运行时在构造函数中初始化,也可以在声明它们时进行初始化。public class MyClass1
{
public readonly double PI2 = 3.14159;
//or
public readonly double PI3;
public MyClass2()
{
PI3 = 3.14159;
}
}
const
不能将它们声明为
static
(它们是隐式静态的)常量的值在编译时求值
常量仅在声明时初始化
readonly
它们可以是实例级别的,也可以是静态的
在运行时对值求值
readonly可以在声明中初始化或通过构造函数
评论
它们不能是静态的,它们是静态的。如果您的意思是不能声明静态const int i = 0,则应弄清楚;
– nawfal
2013年12月13日20:21在
您能解释为什么不能在方法内部进行const声明吗?
– Minh Tran
17-10-29在17:38
#8 楼
有一个只读的小陷阱。可以在构造函数中多次设置只读字段。即使在两个不同的链式构造函数中设置了值,也仍然允许。
public class Sample {
private readonly string ro;
public Sample() {
ro = "set";
}
public Sample(string value) : this() {
ro = value; // this works even though it was set in the no-arg ctor
}
}
#9 楼
const是编译时常量,而readonly允许在运行时计算值并在构造函数或字段初始化程序中设置。因此,'const'始终是常量,但是一旦分配了'readonly'就是只读。C#团队的Eric Lippert拥有有关不同类型不变性的更多信息
#10 楼
这是另一个说明const如何不是版本安全或与引用类型相关的链接。摘要:
const属性的值设置为compile时间并且在运行时不能更改
Const不能标记为静态-关键字表示它们是静态的,与只读字段不同。
Const不能是除值(原始)类型以外的任何内容
readonly关键字将字段标记为不可更改。但是,可以在类的构造函数中更改该属性
readonly only关键字也可以与static组合使用,以使其作用与const(至少在表面上)相同。当您查看两个IL之间的IL时,有一个明显的区别。const字段在IL中被标记为“ literal”,而readonly是“ initonly”
#11 楼
只读:可以在运行时通过Ctor更改值。但不是通过成员Function
,默认情况下是静态的。不能在任何地方(Ctor,Function,运行时等,无处)更改值
评论
谢谢你不要让我只为这两个小菜读4段...
–唐·奇德尔(Don Cheadle)
18年2月26日在19:11
#12 楼
另一个难题:可以通过反射的“不完整”代码来更改只读值。var fi = this.GetType()
.BaseType
.GetField("_someField",
BindingFlags.Instance | BindingFlags.NonPublic);
fi.SetValue(this, 1);
我可以使用反射在C#中更改私有只读继承字段吗?
#13 楼
我相信所有对象的const
值都是相同的(必须使用文字表达式进行初始化),而readonly
的每个实例化都可以不同... #14 楼
我们办公室的一位团队成员提供了有关何时使用const,静态和只读的以下指导:当您具有在运行时可以知道的类型的变量时,请使用const (字符串文字,整数,双精度,枚举等),您希望类的所有实例或使用者可以访问值不应更改的位置。
当您拥有需要所有实例的数据时,请使用static或类的使用者可以访问值可以更改的地方。
当您拥有一个在运行时(对象)无法识别的类型的变量时,请使用静态只读,该变量需要类的所有实例或使用者有权访问不应更改值的地方。
在拥有实例级别变量时使用只读,在对象创建时将知道该变量不应更改。
最后一点: const字段是静态的,但反之则不正确。
评论
我想你的意思是“逆向”。相反将是“非常量字段不是静态的”。这可能是正确的,也可能不是。相反,“静态字段是(总是)const”不是正确的。
–迈克尔·布莱克本
16-4-27在21:02
#15 楼
它们都是常量,但是在编译时也可以使用const。这意味着区别的一方面是可以将const变量用作属性构造函数的输入,但不能用作只读变量。示例:
public static class Text {
public const string ConstDescription = "This can be used.";
public readonly static string ReadonlyDescription = "Cannot be used.";
}
public class Foo
{
[Description(Text.ConstDescription)]
public int BarThatBuilds {
{ get; set; }
}
[Description(Text.ReadOnlyDescription)]
public int BarThatDoesNotBuild {
{ get; set; }
}
}
#16 楼
何时使用
const
或readonly
const
编译时constant:绝对常数,值是在声明期间设置的,在IL代码本身中
readonly
运行时常量:可以通过配置文件
App.config
在构造函数/ init中设置,但一旦初始化,就无法更改#17 楼
标记为const的变量只不过是强类型的#define宏,在编译时,const变量引用将被内联文字值替换。结果,只能以这种方式使用某些内置的原始值类型。标记为只读的变量可以在构造函数中在运行时设置,并且它们的只读性也可以在运行时强制执行。与此相关的性能开销很小,但这意味着您可以对任何类型(甚至是引用类型)使用只读。此外,const变量本质上是静态的,而如果需要,只读变量可以是实例特定的。
评论
补充说,const是强类型的#define宏。否则,我们可能会吓跑所有C或C ++人员。 :-)
–杰森·贝克(Jason Baker)
09年1月5日在22:35
#18 楼
CONSTconst关键字可以应用于字段或局部变量
声明时必须分配const字段
没有分配内存,因为const值嵌入在IL中编译后自行编码。
就像查找所有出现的const变量并替换为其值。
因此,编译后的IL代码将具有硬编码的值来代替const变量
Const默认情况下,C#中的static是static。
对于所有对象,该值都是常量
存在dll版本问题-这意味着,每当我们更改公共const变量或属性时,(实际上,理论上就不应更改它)使用此变量的dll或程序集必须重新构建
仅C#内置类型可以声明为常量
常量字段不能作为ref或out参数传递
ReadOnly
readonly关键字仅适用于非局部变量的字段
我们可以在声明时或在构造函数中分配只读字段,而不能在任何其他方法中使用。
为只读字段分配的动态内存,我们可以在运行时获取值。
只读属于创建的对象,因此只能通过类的实例进行访问。要使其成为类成员,我们需要在readonly之前添加static关键字。
根据所使用的构造函数,该值可能会有所不同(因为它属于该类的对象)
如果声明了非基本类型(引用类型),因为只读引用是不可变的,而不是包含的对象。
由于在运行时获取了该值,因此只读字段/属性没有dll版本控制问题。
我们可以将readonly字段传递为构造函数上下文中的ref或out参数。
#19 楼
另一个陷阱。由于const实际上仅适用于基本数据类型,因此,如果要使用类,可能会感到“被迫”使用ReadOnly。但是,请当心陷阱! ReadOnly表示您不能用另一个对象替换该对象(不能使其引用另一个对象)。但是任何引用该对象的进程都可以自由修改该对象内部的值!所以请不要误以为ReadOnly意味着用户无法更改任何内容。 C#中没有简单的语法来防止类的实例更改其内部值(据我所知)。
评论
是的,那更像是一个一般主题。如果您拥有暴露数组列表的仅获取属性,则仍可以修改该数组列表。您不能为该属性设置其他数组列表,但是不能阻止用户更改数组列表。
– Gishu
08/09/17在13:31
#20 楼
必须对const
进行硬编码,因为可以在类的构造函数中设置readonly
。#21 楼
C#.Net中的const和readonly字段之间存在显着差异const默认情况下是静态的,需要使用常量值初始化,此后无法修改。构造函数中也不允许更改值。不能与所有数据类型一起使用。对于exDateTime。不能与DateTime数据类型一起使用。
public const DateTime dt = DateTime.Today; //throws compilation error
public const string Name = string.Empty; //throws compilation error
public readonly string Name = string.Empty; //No error, legal
readonly可以声明为静态,但不是必需的。声明时无需初始化。可以使用构造函数分配或更改其值。因此,当用作实例类成员时,它具有优势。两种不同的实例可能具有不同的只读字段值。对于ex-
class A
{
public readonly int Id;
public A(int i)
{
Id = i;
}
}
,那么只读字段可以使用即时特定值初始化,如下所示:
A objOne = new A(5);
A objTwo = new A(10);
这里,实例objOne的readonly字段值为5,而objTwo的值为10。使用const无法实现。
#22 楼
一个常量将作为文字值编译到使用者中,而静态字符串将用作对定义值的引用。作为练习,尝试创建一个外部库并在控制台应用程序中使用它,然后更改库中的值并重新编译(不重新编译使用者程序),将DLL放到目录中并手动运行EXE,您应该发现常量字符串不会改变。
评论
我真心怀疑那是真的...我去检查一下。
–ljs
09年9月8日在12:23
这是改善C#的50种特定方法之一-amazon.co.uk/Effective-Specific-Ways-Improve-Your/dp/0321245660 / ...
–拉斯·卡姆
09年9月8日在12:24
my.safaribooksonline.com/0321245660/…
–拉斯·卡姆
09年9月8日在12:27
@Andrew Hare-是的,我刚刚检查了一下。我很惊讶,这是一个真正的陷阱,对此我感到非常惊讶,真是那样……!
–ljs
09年9月8日在12:29
但是,我确实反对在这里使用单词指针。它不是指针,而是引用,C#中存在差异,因为您可以在不安全模式下操作非托管指针,因此区分两者很重要。
–ljs
09年9月8日在12:31
#23 楼
原则上;您可以在运行时将值分配给静态只读字段,并将其分配给非常量值,而必须为const分配常量。#24 楼
常量和只读是相似的,但是它们并不完全相同。 const字段是编译时常量,表示可以在编译时计算该值。只读字段可启用其他方案,在该方案中,必须在类型构造期间运行某些代码。构建后,无法更改只读字段。例如,可以使用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 = r;
green = g;
blue = 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 = r;
green = g;
blue = 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的开发人员指定意图。这样可以实现更好的版本控制行为和更好的性能。
#25 楼
ReadOnly:该值只能从类的构造函数中初始化一次。#26 楼
不同之处在于,静态只读字段的值是在运行时设置的,因此对于程序的不同执行,它可以具有不同的值。但是,const字段的值设置为编译时间常数。请记住:
对于引用类型,在两种情况下(静态和实例),只读修饰符都只能阻止您将新引用分配给该字段。具体来说,它不会使引用所指向的对象不可变。
有关详细信息,请参阅有关此主题的C#常见问题解答: csharpfaq / archive / 2004/12/03 / 274791.aspx
#27 楼
常量变量在编译时声明和初始化。病房之后无法更改该值。只读变量将仅从类的静态构造函数中初始化。只读仅在我们要在运行时分配值时使用。#28 楼
人们上面所说的补充一件事。如果您有一个包含只读值的程序集(例如,readonly MaxFooCount = 4;),则可以通过提供具有不同值的该程序集的新版本(例如,只读MaxFooCount = 5;)来更改调用程序集所看到的值。 >但是有了const,它将在编译调用方时被折叠到调用方的代码中。
如果您已经达到了C#熟练程度,就可以为Bill Wagner的书做好准备,有效的C#:50种改善C#的特定方法
(其中有49则详细回答了这个问题)。
#29 楼
关键区别在于Const是#DEFINE的C等效项。该数字从字面上被替换为a-la预编译器。只读实际上被视为变量。当您拥有项目A依赖于项目B中的公共常量时,这种区别尤其重要。假设公共常量发生了变化。现在,您选择的const / readonly将影响项目A的行为:
Const:项目A不会捕获新值(当然,除非使用新const重新编译它),因为它已编译
ReadOnly:项目A总是向项目B询问变量值,因此它将在B中获取公共常量的新值。
老实说,我建议您对除真正通用常量(例如Pi,Inches_To_Centimeters)以外的几乎所有内容都使用只读。对于可能会更改的任何内容,我说使用readonly。
希望这会有所帮助,
Alan。
#30 楼
常量:在应用程序生命周期内的绝对常数。只读:可以在运行期间更改。
评论
您对它可以更改的“只读”的定义存在缺陷。我猜“更改”是指“设置”,例如“可以在运行时设置”。
–艾哈迈德(Ahmed)
19/12/26在5:48
评论
我不得不低头看几个答案才能找到此链接,但这是一个很好的链接。埃里克·利珀特(Eric Lippert)对C#不变性的看法@donstack,实际上根据C#参考,可以在字段声明和构造函数中多次分配和重新分配只读字段。