将成员变量声明为只读有什么好处?它只是防止有人在类的生命周期内更改其值,还是使用此关键字会提高速度或效率?
readonly
,结构类型的字段与不可变的可变字段相比,会带来性能损失,因为调用readonly
值类型字段的任何成员都会导致编译器复制该字段并调用该字段。成员。
将成员变量声明为只读有什么好处?它只是防止有人在类的生命周期内更改其值,还是使用此关键字会提高速度或效率?
readonly
,结构类型的字段与不可变的可变字段相比,会带来性能损失,因为调用readonly
值类型字段的任何成员都会导致编译器复制该字段并调用该字段。成员。
Answers:
我认为使用只读字段不会带来任何性能提升。这只是一项检查,以确保一旦完全构造了对象,该字段就无法指向新值。
但是,“ readonly”与其他类型的只读语义有很大不同,因为它是在运行时由CLR强制执行的。readonly关键字编译为.initonly,可通过CLR进行验证。
此关键字的真正优点是生成不可变的数据结构。一旦定义,不变的数据结构就无法更改。这使得在运行时对结构的行为进行推理非常容易。例如,不存在将不变结构传递给代码的另一个随机部分的危险。他们无法更改它,因此您可以针对该结构进行可靠编程。
这是关于不变性的好处之一的好文章:线程化
使用并没有明显的性能优势readonly
,至少我从未见过任何地方提到过。它仅用于完全按照您的建议进行,防止在初始化后进行修改。
因此,这样做的好处是可以帮助您编写更健壮,更易读的代码。当您在团队中工作或进行维护时,此类事情的真正好处就体现了。声明某些readonly
内容类似于在代码中为该变量的使用订立合同。您可以将其视为与其他关键字(如internal
或)添加文档的方式相同private
,即表示“初始化后不应修改此变量”,而且执行它。
因此,如果您创建一个类并readonly
通过设计标记一些成员变量,则可以防止自己或其他团队成员稍后在扩展或修改您的类时犯错。在我看来,这是值得拥有的好处(以doofledorfer在评论中提到的额外的语言复杂性为代价)。
用非常实际的话来说:
如果您在dll A中使用const,并且dll B引用了该const,则该const的值将被编译到dll B中。如果使用该const的新值重新部署dll A,则dll B仍将使用原始值。
如果在只读的dll A和dll B引用中使用只读,则始终在运行时查找该只读。这意味着,如果用只读的新值重新部署dll A,则dll B将使用该新值。
readonly
字段中的能力。您不能将a存储在new object();
中const
,这是有道理的,因为您不能在不更改标识的情况下将非值对象(例如,对其他程序集中的引用)进行烘烤。
潜在的情况是编译器可以根据readonly关键字的存在进行性能优化。
仅当readonly字段也标记为static时,这才适用。在这种情况下,JIT编译器可以假定此静态字段永远不会更改。JIT编译器在编译类的方法时可以考虑到这一点。
典型示例:您的类可能具有在构造函数中初始化的静态只读IsDebugLoggingEnabled字段(例如,基于配置文件)。一旦实际方法被JIT编译,则在未启用调试日志记录时,编译器可能会省略代码的整个部分。
我还没有检查这种优化是否实际上在当前版本的JIT编译器中实现,所以这仅仅是猜测。
不要忘记有一种变通方法,可以readonly
使用以下任何构造函数获取字段集out
params。
有点混乱,但:
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
out
..
令人惊讶的是,正如Jon Skeet在测试他的Noda Time库时发现的那样,只读实际上会导致代码变慢。在这种情况下,删除只读后仅用了4秒即可运行20秒的测试。
readonly struct
C#7.2 中的类型,则使该字段成为非只读的好处就消失了。
添加一个基本方面来回答这个问题:
通过省略set
运算符,可以将属性表示为只读。因此,在大多数情况下,您无需将readonly
关键字添加到属性中:
public int Foo { get; } // a readonly property
与此相反:字段需要readonly
关键字才能达到类似的效果:
public readonly int Foo; // a readonly field
因此,将字段标记为的一个好处是readonly
可以在没有set
运算符的情况下获得与属性相似的写保护级别-无需出于任何原因而将字段更改为属性。
注意私有只读数组。如果将这些作为客户端公开(作为对象,您可以像我一样对COM互操作执行此操作),则客户端可以操纵数组值。返回数组作为对象时,请使用Clone()方法。
ReadOnlyCollection<T>
而不是一个数组。
ImmutableArray<T>
,避免将其装箱到界面(IReadOnlyList<T>
)或包装在类(ReadOnlyCollection
)中。它的性能与天然阵列:blogs.msdn.microsoft.com/dotnet/2013/06/24/...
使用只读标记的另一个有趣的部分可以是保护字段免受单例初始化。
例如来自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()
{
}
}
readonly在防止字段Singleton初始化两次方面起着很小的作用。另一个细节是,对于上述情况,您不能使用const,因为const在编译时强制创建,但是singleton在运行时进行创建。
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 ;
}
}
}