C#中的constreadonly有什么区别?

什么时候可以使用另一个?

评论

我不得不低头看几个答案才能找到此链接,但这是一个很好的链接。埃里克·利珀特(Eric Lippert)对C#不变性的看法

@donstack,实际上根据C#参考,可以在字段声明和构造函数中多次分配和重新分配只读字段。

#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 楼



何时使用constreadonly



const



编译时constant:绝对常数,值是在声明期间设置的,在IL代码本身中



readonly



运行时常量:可以通过配置文件App.config在构造函数/ init中设置,但一旦初始化,就无法更改






#17 楼

标记为const的变量只不过是强类型的#define宏,在编译时,const变量引用将被内联文字值替换。结果,只能以这种方式使用某些内置的原始值类型。标记为只读的变量可以在构造函数中在运行时设置,并且它们的只读性也可以在运行时强制执行。与此相关的性能开销很小,但这意味着您可以对任何类型(甚至是引用类型)使用只读。

此外,const变量本质上是静态的,而如果需要,只读变量可以是实例特定的。

评论


补充说,const是强类型的#define宏。否则,我们可能会吓跑所有C或C ++人员。 :-)

–杰森·贝克(Jason Baker)
09年1月5日在22:35

#18 楼

CONST


const关键字可以应用于字段或局部变量
声明时必须分配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