在C#中将字段标记为`readonly`有什么好处?
将成员variables声明为只读的有什么好处? 是否只是在类的生命周期中防止某些人改变,或者是否由于这个关键字而改变了编译速度?
readonly
关键字用于声明一个常量的成员variables,但允许在运行时计算该值。 这与使用const
修饰符声明的const
,后者必须在编译时设置其值。 使用readonly
,可以在声明中或在字段所属对象的构造函数中设置字段的值。
如果您不想重新编译引用该常量的外部DLL(因为它在编译时被replace),也可以使用它。
我不相信使用只读字段会有性能提升。 这只是一个检查,以确保一旦对象完全构build,该字段不能被指向一个新的值。
然而,“只读”与其他types的只读语义是非常不同的,因为它是由CLR在运行时执行的。 readonly关键字编译为.initonly,这可以由CLRvalidation。
这个关键字的真正优点是生成不可变的数据结构。 根据定义,不可变数据结构一旦构build就不能改变。 这使得在运行时很容易推断结构的行为。 例如,将不可变结构传递给另一个随机部分的代码是没有危险的。 他们不能改变它,所以你可以可靠地编程这个结构。
这是关于不变性的好处之一: 线程
使用readonly
方式没有明显的性能优势,至less没有我见过的任何地方提到过。 这只是为了完全按照你的build议,为了防止一旦它被初始化修改。
所以它有助于编写更健壮,更易读的代码。 这样的事情真正的好处来自于你在一个团队工作或维护。 声明为readonly
类似于在代码中为该variables的使用合约。 把它看作是像其他关键字(如internal
或private
一样添加文档,你会说“这个variables在初始化之后不应该被修改”,而且你正在执行它。
所以,如果你创build一个类,并按devisereadonly
标记一些成员variables,那么你可以防止自己或其他团队成员稍后在扩展或修改你的类时犯错误。 在我看来,这是一个值得的好处(只是在评论中提到的语言复杂程度的额外的语言复杂性的小花费)。
说得很实际:
如果在dll A和dll B引用const中使用const,则该const的值将被编译为dll B.如果您为该const重新部署dll A,则dll B将仍然使用原始值。
如果您在只读的dll A和dll B引用中使用readonly,那么readonly将始终在运行时查找。 这意味着,如果你重新部署DLL的一个新的值只读,DLL B将使用该新的值。
编译器可能会根据readonly关键字的存在情况进行性能优化。
这只适用于只读字段也被标记为静态 。 在这种情况下,JIT编译器可以假定这个静态字段永远不会改变。 JIT编译器可以在编译类的方法时考虑到这一点。
典型的例子:你的类可以有一个静态的只读IsDebugLoggingEnabled字段,在构造函数中初始化(例如基于configuration文件)。 一旦实际的方法被JIT编译,当debugging日志没有被启用时,编译器可能会省略整个代码部分。
我没有检查过这个优化是否在当前版本的JIT编译器中实际实现,所以这只是推测。
请记住,readonly仅适用于值本身,所以如果使用只读引用types,只会保护参考不被更改。 实例的状态不受readonly的保护。
不要忘记,有一个解决方法来获取在任何构造函数之外使用params设置的readonly
字段。
有点混乱,但:
private readonly int _someNumber; private readonly string _someText; public MyClass(int someNumber) : this(data, null) { } public MyClass(int someNumber, string someText) { Initialise(out _someNumber, someNumber, out _someText, someText); } private void Initialise(out int _someNumber, int someNumber, out string _someText, string someText) { //some logic }
进一步讨论在这里: http : //www.adamjamesnaylor.com/2013/01/23/Setting-Readonly-Fields-From-Chained-Constructors.aspx
readonly
可以在声明时初始化,或者只从构造函数获取它的值。 与const
不同,它必须被初始化并同时声明。 readonly
有一切 const
,加上构造函数的初始化
using System; class MainClass { public static void Main (string[] args) { Console.WriteLine(new Test().c); Console.WriteLine(new Test("Constructor").c); Console.WriteLine(new Test().ChangeC()); //Error A readonly field // `MainClass.Test.c' cannot be assigned to (except in a constructor or a // variable initializer) } public class Test { public readonly string c = "Hello World"; public Test() { } public Test(string val) { c = val; } public string ChangeC() { c = "Method"; return c ; } } }
小心私人只读数组。 如果这些客户端作为对象公开(客户端可以像我这样做COM互操作),则客户端可以操作数组值。 将数组作为对象返回时使用Clone()方法。
如果你有一个预先定义或预先计算的值需要保持相同的整个程序,那么你应该使用常量,但如果你有一个值,需要在运行时提供,但一旦分配应保持相同的整个程序,你应该使用只读。 例如,如果您必须分配程序开始时间,或者必须在对象初始化时存储用户提供的值,并且必须限制它进一步更改,则应该只使用readonly。
在WPF中可以有性能优势,因为它消除了昂贵的DependencyProperties的需要。 这对collections尤其有用
只读标记使用的另一个有趣的部分可以是保护字段在单例中的初始化。
例如在csharpindepth的代码中:
public sealed class Singleton { private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton()); public static Singleton Instance { get { return lazy.Value; } } private Singleton() { } }
只读起到保护字段Singleton不受初始化两次的小作用。 另一个细节是,对于上面提到的场景,你不能使用const,因为const在编译时强制创build,而singleton在运行时创build。