提问



const readonly 之间的区别是什么?你使用的是另一个吗?

最佳参考


除了明显的差异



  • 必须声明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值的情况下,它就像一个find-replace,值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. precision)..或者有疑问,请使用readonly


public readonly float PI = 3.14;


更新:Aku需要得到一个提及因为他首先指出了这一点。另外我需要插上我学到的东西..有效的C# - Bill Wagner

其它参考1


有一个充满争议的问题!如果从另一个程序集引用常量,则其值将被编译到调用程序集中。这样,当您更新引用的程序集中的常量时,它不会在调用程序集中进行更改!

其它参考2


常量




  • 默认情况下,常量是静态的

  • 他们必须在编译时有一个值(你可以有例如3.14 * 2,但不能调用方法)

  • 可以在函数
  • 中声明
  • 被复制到使用它们的每个程序集中(每个程序集都获取值的本地副本)

  • 可以在属性中使用



只读实例字段




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



静态只读字段




  • 在代码执行遇到类引用时(在创建新实例或执行静态方法时)进行评估

  • 静态构造函数完成时必须具有评估值

  • 不建议将ThreadStaticAttribute放在这些上(静态构造函数将仅在一个线程中执行,并将为其线程设置值;所有其他线程将使此值未初始化)


其它参考3


只是要添加,ReadOnly用于引用类型只使引用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
  }
}

其它参考4


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


编辑:看到Gishu上面的细微差别

其它参考5


const:任何地方都无法改变。


readonly:此值只能在构造函数中更改。在正常功能中无法改变。

其它参考6


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


C#团队的Eric Lippert有关于不同类型的不变性的更多信息[53]

其它参考7


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



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

其它参考8


常量成员在编译时定义,不能在运行时更改。常量使用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;
     }
}


常量



  • 它们不能声明为static(它们是隐式静态的)

  • 在编译时评估constant的值

  • 常量仅在声明中初始化



只读



  • 它们可以是实例级或静态

  • 在运行时评估该值

  • readonly可以在声明中或在构造函数
  • 中的代码中初始化

其它参考9


这是另一个链接,展示const不是版本安全的,或者与参考类型相关。[54]


摘要:



  • const属性的值在编译时设置,不能在运行时更改

  • Const不能标记为静态 - 关键字表示它们是静态的,不像readonly字段那样。

  • Const不能是值(原始)类型
  • 之外的任何东西
  • readonly关键字将该字段标记为不可更改。但是,可以在类
  • 的构造函数内更改属性
  • readonly only关键字也可以与static结合使其以与const(表面上至少)相同的方式运行。当你看两个
  • 之间的IL时,会有明显的不同
  • const字段在IL中标记为literal,而readonly是initonly


其它参考10


还有另一个问题:只能通过反射通过狡猾代码来改变只读值。


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


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

其它参考11


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


常量:
通过defult静态。价值无法从任何地方改变(Ctor,Function,runtime等no-where)

其它参考12


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

其它参考13


我们办公室的一名团队成员就何时使用const,static和readonly提供了以下指导:



  • 当你有一个你可以在运行时知道的类型的变量(字符串文字,整数,双数,枚举......)时,使用 const 你想要一个类的所有实例或消费者可以访问价值不应该改变的地方。

  • 当您拥有希望类的所有实例或使用者都可以访问值可以更改的位置的数据时,请使用 static

  • 如果您拥有在运行时无法知道的类型变量(对象),您希望类的所有实例或使用者都可以访问值不应更改的位置,请使用静态只读

  • 当您拥有一个实例级别变量时,使用 readonly ,您将在创建对象时知道该变量不应更改。



最后要注意的是:const字段是静态的,但反之则不然。

其它参考14


它们都是常量,但在编译时也可以使用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; }
  }
}

其它参考15


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


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

其它参考16


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


public static readonly uint l1 = (uint) DateTime.Now.Ticks;

其它参考17


另一个陷阱


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


因此,不要认为ReadOnly意味着用户无法改变事物。 C#中没有简单的语法来防止类的实例化使其内部值发生变化(据我所知)。

其它参考18


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


const默认是静态的,需要用常量值初始化,以后不能修改。构造函数中也不允许更改值。它不能与所有数据类型一起使用。对于前日期时间。它不能与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可以声明为static,但不是必需的。无需在声明时初始化。可以使用构造函数指定或更改其值。因此,它在用作实例类成员时具有优势。两个不同的实例化可能具有不同的只读字段值。对于前 -


class A
{
    public readonly int Id;

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


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


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


这里,实例objOne将readonly字段的值设为5,objTwo的值为10.使用const是不可能的。

其它参考19


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


public static readonly uint timeStamp = (uint)DateTime.Now.Ticks;

其它参考20


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


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

其它参考21


常数


我们需要在定义时为const字段提供值。然后,编译器将常量的值保存在程序集的元数据中。这意味着只能为基本类型定义常量,如boolean,char,byte等。常量始终被视为静态成员,而不是实例成员。


只读


只读字段只能在运行时解析。这意味着我们可以使用构造函数为声明字段的类型定义值。验证由编译器完成,只读字段不是由构造函数以外的任何方法写入的。


关于这两者的更多内容在本文[56]中进行了解释

其它参考22


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

其它参考23


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

其它参考24


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


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


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


因为像3.14和0这样的值是编译时常量。但是,请考虑您定义类型并希望提供一些预制实例的情况。例如,您可能希望定义一个Color类并为常用颜色(如Black,White等)提供常量。不可能使用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的客户端使用它。不用说,这会导致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成员总是静态的,而readonly成员可以是静态的,也可以不是静态的,就像常规字段一样。


可以将单个关键字用于这两个目的,但这会导致版本控制问题或性能问题。假设我们为此(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:该值只从类的构造函数初始化一次
const:可以在任何函数中初始化,但只能初始化一次

其它参考26


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


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


有关详细信息,请参阅有关此主题的C#常见问题解答:
http://blogs.msdn.com/csharpfaq/archive/2004/12/03/274791.aspx[57]

其它参考27


在编译时声明和初始化常量变量。病房后无法更改该值。只读变量将仅从类的静态构造函数初始化。只有在我们想要在运行时分配值时才使用只读。

其它参考28


const字段只能在字段声明时初始化。 readonly字段可以在声明或构造函数中初始化。

其它参考29


添加人们上面所说的一件事。如果你有一个包含readonly值的程序集(例如readonly MaxFooCount=4;),你可以通过运送具有不同值的该程序集的新版本来更改调用程序集看到的值(例如,readonly MaxFooCount=5;)


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


如果你达到了C#熟练程度,那么你已经准备好了Bill Wagner的书,有效的C#:50种提高你C#的具体方法
这个问题详细解答了这个问题(以及其他49个问题)。