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

1692

constreadonly 在 C# 中有什么区别?

在什么情况下会使用其中一个而不是另一个?


4
我需要查看好几个答案才找到这个链接,但它很不错。Eric Lippert在C#中关于不可变性的看法。 - Frank Bryce
5
@donstack,根据C# 参考文档的说明,只读字段可以在字段声明和构造函数中被赋值和重新赋值多次。 - Marques
这里是const和readonly的详细解释: https://youtu.be/jA30qZNGNoM - Parveen
30个回答

3

另一个需要注意的地方是,由于const只适用于基本数据类型,如果你想使用类,则可能会感到“被迫”使用ReadOnly。但是,请注意陷阱!ReadOnly意味着您不能将对象替换为另一个对象(无法使其引用另一个对象)。但是,任何持有对象引用的进程都可以自由地修改对象内部的值!

因此,请不要被误导认为ReadOnly意味着用户无法更改事物。据我所知,在C#中没有简单的语法可以防止实例化的类的内部值被更改。


是的,那更多是一个通用主题。如果您有一个只读属性暴露一个ArrayList,您仍然可以修改ArrayList。您不能将不同的ArrayList设置为该属性,但您无法阻止用户更改ArrayList。 - Gishu

2

const和readonly有相似之处,但它们并不完全相同。const字段是编译时常量,意味着该值可以在编译时计算。readonly字段允许在类型构造期间运行某些代码的其他场景。构造完成后,readonly字段不能被更改。

例如,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类的其他客户端带来困扰。只读特性解决了这个问题。通过在声明中引入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 的开发人员指定意图。这有助于更好的版本控制行为和更好的性能。

2
静态只读字段的值在运行时设置,因此对程序的不同执行可以有不同的值。但是,常量字段的值被设置为编译时常量。
请记住: 对于引用类型,在静态和实例两种情况下,readonly修饰符仅防止您将新引用分配给字段。它特别不能使被引用的对象成为不可变对象。
有关详细信息,请参阅C#常见问题解答: http://blogs.msdn.com/csharpfaq/archive/2004/12/03/274791.aspx

2

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


2

常量将被编译为字面值,而静态字符串将作为对所定义的值的引用。

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


我真的怀疑那是真的...我会去检查一下。 - ljs
http://my.safaribooksonline.com/0321245660/pref01#X2ludGVybmFsX1NlY3Rpb25Db250ZW50P3htbGlkPTAzMjEyNDU2NjAvY2gwMWxldjFzZWMy - Russ Cam
@Andrew Hare - 是的,我刚刚检查了。我非常惊讶,这是一个真正的陷阱,我真的非常惊讶,惊讶于事实...! - ljs
我反对在这里使用指针这个词。它不是指针,而是引用,在C#中有一个区别,因为您可以在不安全模式下操作未管理的指针,所以区分两者非常重要。 - ljs
更不用说值本身是不可变的,所以你不能改变它(好吧,除非进行一些奇怪的邪恶的暗中操作)。无论如何 :) - ljs

1

通常情况下,您可以在运行时将静态只读字段分配给非常量值,而常量必须分配一个常量值。


0

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

Readonly: 可以在运行时更改。


2
你对“只读(Readonly)”的定义存在缺陷,它是可以改变的。我猜你所说的“改变”是指“设置”,比如“它可以在运行时被设置”。 - Ahmed

0
一个要补充的内容是,如果您有一个包含只读值的程序集(例如 readonly MaxFooCount = 4;),您可以通过使用具有不同值的新版本程序集(例如 readonly MaxFooCount = 5;)来更改调用程序集看到的值。
但对于 const,它将在编译调用者代码时折叠成调用者的代码中。
如果您已经达到了C#专业水平,那么可以阅读Bill Wagner的书《Effective C#: 50 Specific Ways to Improve Your C#》,其中详细回答了这个问题(以及其他49个问题)。

0

关键区别在于Const是C语言中#DEFINE的等效物。数字字面上被替换为预编译器。Readonly实际上被视为变量。

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

Const:除非使用新的const重新编译,否则项目A不会捕获新值,因为它是使用常量替换编译的。

ReadOnly:项目A将始终请求项目B的变量值,因此它将获取B中公共常量的新值。

老实说,我建议您几乎在所有情况下都使用readonly,除了真正通用的常量(例如Pi,Inches_To_Centimeters)。对于任何可能更改的内容,我建议使用readonly。

希望这有所帮助, 艾伦。


0

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

在只读字段中,我们可以在声明时或该类的构造函数中分配值。对于常量,我们只能在声明时分配值。

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


只能使用readonly修饰符与static修饰符一起使用,但是constant不能与static一起使用。这是因为const字段已经隐式地是静态的。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接