C#中const和readOnly有什么区别?

C#中#0#1有什么区别?

你什么时候会使用一个而不是另一个?

492574 次浏览

我相信const值对于所有对象都是相同的(并且必须使用文字表达式初始化),而readonly对于每个实例化都可能不同…

这就解释了。总结:const必须在声明时初始化,readOnly可以在构造函数上初始化(因此根据使用的构造函数具有不同的值)。

编辑:请参阅上面Gishu的问题来了解细微的差异

const是编译时常量,而readOnly允许在运行时计算值并在构造函数或字段初始化器中设置。因此,“const”始终是常量,但“readOnly”在分配后是只读的。

C#团队的Eric Lippert对不同类型的不变性有更多信息

有一个带有consts的陷阱!如果您从另一个程序集中引用一个常量,它的值将直接编译到调用程序集中。这样,当您在引用程序集中更新常量时,它不会在调用程序集中更改!

除了明显的差异

  • 必须在定义const VSreadonly值时声明值可以动态计算,但需要在构造函数退出之前分配。之后它被冻结。
  • 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,#1仍然会有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的变量只不过是强类型的#定义宏,在编译时const变量引用被替换为内联文字值。因此,只有某些内置的原始值类型可以以这种方式使用。标记为只读的变量可以在运行时在构造函数中设置,并且它们的只读性也可以在运行时强制执行。与此相关的性能成本较小,但这意味着您可以对任何类型(甚至引用类型)使用只读。

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

只是为了补充一下,引用类型的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 valueI_RO_VALUE = new char[]{'V'}; //will cause compiler error}}

除了上面提到的之外,还有一件事要补充。如果你有一个包含只读值的程序集(例如只读MaxFoCount=4;),你可以通过发送一个具有不同值的新版本的程序集来更改调用程序集看到的值(例如只读MaxFoCount=5;)

但是使用const,它将在编译调用者时折叠到调用者的代码中。

如果您已经达到了C#熟练程度,那么您就可以阅读Bill Wagner的书,有效的C#:50种改进C#的具体方法它详细回答了这个问题(以及其他49件事)。

关键区别在于Const是#DEFINE的C等价物。该数字实际上被替换为a-la预编译程序。ReadOnly实际上被视为变量。

当您的项目A依赖于项目B中的公共常量时,这种区别尤其相关。假设公共常量发生变化。现在,您对const/readOnly的选择将影响项目A的行为:

const:项目A不会捕获新值(当然,除非它使用新的const重新编译),因为它是使用子属性中的常量编译的。

只读:项目A总是会向项目B询问它的变量值,所以它会在B中获取公共常量的新值。

老实说,除了真正的通用常量(例如Pi,Inches_To_Centimeters)之外,我建议你几乎所有的东西都使用只读。对于任何可能改变的东西,我说使用只读。

希望能有所帮助,艾伦

它们都是常量,但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; }}}

另一个明白了

由于const实际上只适用于基本的数据类型,如果你想使用一个类,你可能会觉得“被迫”使用ReadOnly。然而,小心陷阱!ReadOnly意味着你不能用另一个对象替换对象(你不能让它引用另一个对象)。但是任何对该对象有引用的进程都可以自由修改对象里面的值!

所以不要以为ReadOnly意味着用户不能改变东西。C#中没有简单的语法来防止类的实例化更改其内部值(据我所知)。

我们办公室的一位团队成员提供了以下关于何时使用const、静态和只读的指导:

  • 当您有一个变量类型时,您可以在运行时知道(字符串文字、int、双精度、枚举等),您希望类的所有实例或消费者都可以访问该值不应更改的位置时,请使用const
  • 当您拥有希望类的所有实例或使用者都可以访问值可以更改的数据时,请使用静态
  • 当您有一个在运行时无法知道的类型的变量(对象)时,请使用静态只读,您希望类的所有实例或消费者都可以访问该值不应更改的位置。
  • 当您有一个实例级变量时,请使用只读,您将在创建对象时知道该变量不应更改。

最后一个注意事项:const字段是静态的,但反过来不是真的。

只读有一个小问题。只读字段可以在构造函数中多次设置。即使该值在两个不同的链式构造函数中设置,它仍然是允许的。

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}}

常量

  • 常量默认为静态
  • 它们在编译时必须有一个值(例如3.14*2,但不能调用方法)
  • 可以在函数中声明
  • 被复制到使用它们的每个程序集中(每个程序集都获得值的本地副本)
  • 可以在属性中使用

只读实例字段

  • 在构造函数退出时必须有设置的值
  • 在创建实例时被评估

静态只读字段

  • 当代码执行命中类引用时(创建新实例或执行静态方法时)评估
  • 在静态构造函数完成时必须有一个评估值
  • 不建议在这些线程上放置Thread静态属性(静态构造函数将仅在一个线程中执行,并为其线程设置值;所有其他线程都将未初始化此值)

这是另一个链接演示const如何不是版本安全的,或者与引用类型相关。

总结

  • const属性的值在编译时设置,在运行时不能更改
  • const不能被标记为静态-关键字表示它们是静态的,不像只读字段可以。
  • const只能是值(原始)类型
  • readOnly关键字将该字段标记为不可更改。但是,可以在类的构造函数中更改该属性
  • readOnly关键字也可以与静态组合,使其以与const相同的方式起作用(至少在表面上)。当你查看两者之间的IL时,会有明显的差异
  • const字段在IL中标记为“字面量”,而readOnly是“initOnly”

常量将作为文本值编译到消费者中,而静态字符串将作为对定义值的引用。

作为练习,尝试创建一个外部库并在控制台应用程序中使用它,然后更改库中的值并重新编译它(不重新编译消费者程序),将DLL放入目录并手动运行EXE,您应该会发现常量字符串没有变化。

原则上;您可以在运行时将静态只读字段的值分配给非常量值,而const必须分配一个常量值。

还有一个问题:只读值可以通过反射被“狡猾”的代码更改。

var fi = this.GetType().BaseType.GetField("_someField",BindingFlags.Instance | BindingFlags.NonPublic);fi.SetValue(this, 1);

我可以使用反射更改C#中的私有只读继承字段吗?

const必须是硬编码,其中readonly可以是类的在构造函数中设置

const:不能在任何地方更改。

readonly:此值只能在构造函数中更改。不能在普通函数中更改。

常量成员在编译时定义,不能在运行时更改。常量使用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(它们是隐式静态的)
  • 常量的值在编译时计算
  • 常量仅在声明时初始化

只读

  • 它们可以是实例级的,也可以是静态的
  • 该值在运行时评估
  • 只读可以在声明或构造函数中的代码中初始化

const和readOnly类似,但它们并不完全相同。const字段是编译时常量,这意味着可以在编译时计算该值。只读字段允许在构造类型期间必须运行某些代码的其他方案。构造后,只读字段不能更改。

例如,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 = r;green = g;blue = 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 = 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的开发人员指定意图。这有助于更好的版本控制行为和更好的性能。

ReadOnly:该值只会从类的构造函数初始化一次。
const:可以在任何函数中初始化,但只能初始化一次

不同之处在于静态只读字段的值是在运行时设置的,因此对于程序的不同执行,它可以有不同的值。但是,const字段的值设置为编译时常量。

记住:对于引用类型,在这两种情况下(静态和实例),只读修饰符仅阻止您向字段分配新引用。它特别不会使引用指向的对象不可变。

详情请参考该主题的C#常见问题:http://blogs.msdn.com/csharpfaq/archive/2004/12/03/274791.aspx

C#. Net中const和readOnly字段之间存在显着差异

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

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

只读可以声明为静态,但不是必需的。声明时无需初始化。它的值可以使用构造函数分配或更改。因此,当用作实例类成员时,它具有优势。两个不同的实例化可能具有不同的只读字段值。对于例如-

class A{public readonly int Id;
public A(int i){Id = i;}}

然后只读字段可以使用即时特定值初始化,如下所示:

A objOne = new A(5);A objTwo = new A(10);

在这里,实例对象将具有只读字段的值为5,而对象对象具有10。这在使用const时是不可能的。

只读:值可以在运行时通过Ctor更改。但不能通过成员函数

恒定:默认为静态。值不能从任何地方更改(Ctor、函数、运行时等无位置)

CONST

  1. const关键字可以应用于字段或局部变量
  2. 我们必须在声明时分配const字段
  3. 没有分配内存,因为const值在编译后嵌入到IL代码本身中。这就像找到所有出现的const变量并用它的值替换。所以编译后的IL代码将使用硬编码值代替const变量
  4. 默认情况下,C#中的const是静态的。
  5. 该值对于所有对象都是常量
  6. 存在dll版本控制问题-这意味着每当我们更改公共const变量或属性时(事实上,理论上不应该更改它),任何使用此变量的其他dll或程序集都必须重新构建
  7. 只有C#内置类型可以声明为常量
  8. const字段不能作为ref或out参数传递

只读

  1. readOnly关键字仅适用于字段而不是局部变量
  2. 我们可以在声明时或构造函数中分配只读字段,而不是在任何其他方法中。
  3. 为只读字段分配动态内存,我们可以在运行时获取值。
  4. 只读属于创建的对象,因此只能通过类的实例访问。要使其成为类成员,我们需要在只读之前添加静态关键字。
  5. 值可能因使用的构造函数而异(因为它属于类的对象)
  6. 如果将非基元类型(引用类型)声明为只读引用,则它所包含的对象是不可变的。
  7. 由于该值是在运行时获得的,因此只读字段/属性不存在dll版本控制问题。
  8. 我们可以在构造函数上下文中将只读字段作为ref或out参数传递。

const:应用程序生命周期内的绝对常数值。

只读:它可以在运行时更改。

  • 何时使用constreadonly

    • const

      • 编译时间常量:绝对常量,在声明时设置值,在IL代码本身中
    • readonly

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

只读字段的值可以更改。但是,const字段的值不能更改。

在只读字段中,我们可以在声明时或在该类的构造器中赋值。

只读可以与静态修饰符一起使用,但常量不能与静态一起使用。