ChatGPT解决这个技术问题 Extra ChatGPT

C# 中的 const 和 readonly 有什么区别?

C# 中的 constreadonly 有什么区别?

您什么时候会使用其中一种?

我不得不查看几个答案才能找到此链接,但这是一个很好的答案。 Eric Lippert's take on immutability in C#
@donstack,实际上根据C# reference可以在字段声明和构造函数中多次分配和重新分配只读字段。

O
Olivia Stork

除了明显的区别

必须在定义 const VS 只读值时声明值可以动态计算,但需要在构造函数退出之前分配.. 之后它被冻结。

const 是隐式静态的。您使用 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,直到我重新编译它。

在只读值的情况下,它就像一个内存位置的引用。该值未包含在 AssemblyB 的 IL 中。这意味着如果内存位置被更新,AssemblyB 无需重新编译即可获得新值。所以如果I_RO_VALUE更新为30,只需要构建AssemblyA,不需要重新编译所有客户端。

因此,如果您确信常量的值不会改变,请使用 const

public const int CM_IN_A_METER = 100;

但是,如果您有一个可能会改变的常数(例如精度).. 或者有疑问时,请使用 readonly

public readonly float PI = 3.14;

更新:Aku 需要提及,因为他首先指出了这一点。我还需要插入我学到的地方:Effective C# - Bill Wagner


static 点似乎是最重要和最有用的点 - consts are implicitly static
关于参考值的部分是最重要的部分。常量值可以被优化掉。
readonly 变量可以在构造函数(反射)之外更改。只有编译器试图阻止您在构造函数之外修改 var。
构造函数完成后,即使通过反射,也不允许更改 @mini-me readonly 变量。运行时恰好不强制执行此操作。运行时也碰巧不会强制您不要将 string.Empty 更改为 "Hello, world!",但我仍然不会声称这会使 string.Empty 可修改,或者代码不应假定 string.Empty 将始终是长度为零的字符串。
blogs.msmvps.com/jonskeet/2014/07/16/… 是一个有趣的只读只读开销成本
A
Appulus

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


在反编译(Reflector、ILSpy、..)时,无论是同一个程序集还是另一个程序集,任何人都永远不会引用一个常量,因此您根本无法分析编译代码中常量的用法。
S
Shantanu Gupta

常数

常量默认是静态的

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

可以在函数中声明

被复制到使用它们的每个程序集中(每个程序集都获得值的本地副本)

可用于属性

只读实例字段

必须在构造函数退出时设置值

在创建实例时进行评估

静态只读字段

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

在静态构造函数完成时必须有一个评估值

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


d
d219

只是补充一下,对于引用类型的 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
  }
}

除了 string 之外,还有其他可以用作常量的引用类型吗?
您可以将 const 用于字符串以外的引用类型,但常量只能具有值 null
@user1333 您将取消引用的值与引用的值混淆了。您的示例中的实例(只读值)是 Char[]。您在第一个更新语句中更改的值是 通过 引用访问的内容,而不是引用本身。
V
Vinko Vrsalovic

This explains it。总结: const 必须在声明时初始化,readonly 可以在构造函数上初始化(因此根据使用的构造函数具有不同的值)。

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


A
Appulus

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

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


M
Michael Blackburn

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

常量

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

常量的值在编译时评估

常量仅在声明时初始化

只读

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

该值在运行时评估

readonly 可以在声明中初始化,也可以通过构造函数中的代码初始化


它们不能是静态的,它们是静态的。如果您的意思是 one can't declare static const int i = 0;,您应该清楚说明
您能解释一下为什么不能在方法内进行 const 声明吗?
W
Wai Ha Lee

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

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

W
Wai Ha Lee

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

C# 团队的 Eric Lippert more information 讨论了不同类型的不变性。


C
Chris S

Here's another link 演示 const 如何不是版本安全的,或者与引用类型无关。

概括:

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

const 不能标记为静态 - 关键字表示它们是静态的,与可以的只读字段不同。

const 不能是除了值(原始)类型之外的任何东西

readonly 关键字将该字段标记为不可更改。但是可以在类的构造函数中更改属性

readonly only 关键字也可以与 static 结合使用,使其行为方式与 const 相同(至少在表面上)。当您查看两者之间的 IL 时,存在显着差异

const 字段在 IL 中被标记为“literal”,而 readonly 是“initonly”


d
d219

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

常量:默认为静态。值不能从任何地方更改(Ctor、Function、运行时等无处)


感谢您没有让我仅仅为这两个要点阅读 4 段...
C
Community

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

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

Can I change a private readonly inherited field in C# using reflection?


D
Daren Thomas

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


S
Scott Lawrence

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

当你有一个在运行时可以知道的类型的变量(字符串字面量、整数、双精度、枚举......)你希望类的所有实例或消费者都可以访问值不应更改的位置时,请使用 const。

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

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

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

最后一点: const 字段是静态的,但反之则不正确。


我想你的意思是“交谈”。反过来是“非常量字段不是静态的”。这可能是真的,也可能不是。相反,“静态字段(始终)是常量”是不正确的。
H
Hallgrim

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

M
Muhammad VP

常数

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

只读

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


R
Ryan Efendy

何时使用 const 或只读 const 编译时常量:绝对常量,值在声明期间设置,在 IL 代码本身中 只读运行时常量:可以通过配置文件即 App.config 在构造函数/init 中设置,但是一旦初始化就无法更改

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

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

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

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


J
Jason Baker

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

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


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

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

class A
{
    public readonly int Id;

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

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

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

在这里,实例 objOne 的只读字段值为 5,而 objTwo 的值为 10。使用 const 是不可能的。


M
Mark T

另一个问题。

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

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


是的,这更像是一个普遍的主题。如果你有一个 get only 属性暴露一个 arraylist,你仍然可以修改 arraylist。您不能为该属性设置不同的数组列表,但不能阻止用户更改数组列表。
G
Greg

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


B
Brett Ryan

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

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


我真诚地怀疑这是真的……我会去检查的。
这是改进 C# 的 50 种特定方法之一 - amazon.co.uk/Effective-Specific-Ways-Improve-Your/dp/0321245660/…
@Andrew Hare - 是的,我刚刚检查过。我很惊讶,这是一个真正的陷阱,我真的很惊讶,很惊讶就是这样......!
然而,我反对在这里使用指针这个词。它不是指针,而是引用,并且在 C# 中有所不同,因为您可以在不安全模式下操作非托管指针,因此区分两者很重要。
R
Ramesh Rajendran

const 和 readonly 相似,但并不完全相同。 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 成员始终是静态的,而 readonly 成员可以是静态的,也可以不是静态的,就像常规字段一样。

可以为这两个目的使用单个关键字,但这会导致版本控制问题或性能问题。假设我们为此使用了一个关键字(const)并且开发人员写道:

public class A
{
    public static const C = 0;
}

另一个开发人员编写了依赖于 A 的代码:

public class B
{
    static void Main() {
        Console.WriteLine(A.C);
    }
}

现在,生成的代码可以依赖于 AC 是编译时常量这一事实吗?即,AC 的使用可以简单地用值 0 代替吗?如果您对此说“是”,那么这意味着 A 的开发人员无法更改 AC 的初始化方式——这在未经许可的情况下束缚了 A 的开发人员的手。如果你对这个问题说“不”,那么就会错过一个重要的优化。也许 A 的作者肯定 AC 将永远为零。 const 和 readonly 的使用允许 A 的开发人员指定意图。这有助于更好的版本控制行为和更好的性能。


d
donstack

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


Y
Yonatan Nir

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

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

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


O
Omar AMEZOUG

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


l
ljs

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


A
Anthony

对人们上面所说的内容要补充一件事。如果您有一个包含只读值的程序集(例如只读 MaxFooCount = 4; ),您可以通过发送具有不同值的新版本的程序集(例如只读 MaxFooCount = 5;)来更改调用程序集看到的值

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

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


A
AlanR

主要区别在于Const 是#DEFINE 的C 等价物。该数字实际上被替换为 a-la 预编译器。 Readonly 实际上被视为一个变量。

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

const:项目 A 没有捕获新值(当然,除非它用新的 const 重新编译),因为它是用替换的常量编译的。

只读:项目 A 将始终向项目 B 询问其变量值,因此它将获取 B 中公共常量的新值。

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

希望这会有所帮助,艾伦。


B
Bigeyes

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

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


您对可以更改的“只读”的定义是有缺陷的。我猜“更改”是指“设置”,例如“可以在运行时设置”。