首页 文章

在C#中将字段标记为“只读”有什么好处?

提问于
浏览
228

将成员变量声明为只读有什么好处?它只是防止在类的生命周期中更改某些人,或者是否由于此关键字而导致编译器速度提高

14 回答

  • 164

    我不相信使用只读字段会有任何性能提升 . 它只是一个检查,以确保一旦完全构造对象,该字段不能指向新值 .

    然而,“readonly”与其他类型的只读语义非常不同,因为它在运行时由CLR强制执行 . readonly关键字编译为.initonly,可由CLR验证 .

    此关键字的真正优势是生成不可变数据结构 . 根据定义,不可变数据结构一旦构造就不能改变 . 这使得在运行时很容易推断出结构的行为 . 例如,没有将不可变结构传递给另一个随机代码部分的危险 . 它们无法改变它,因此您可以针对该结构进行可靠的编程 .

    关于不变性的好处之一,这是一个很好的条目:Threading

  • 41

    如果你有一个预先定义或预先计算的值需要在整个程序中保持相同,那么你应该使用常量,但是如果你有一个需要在运行时提供的值但是一旦分配了整个程序就应该保持相同你应该使用只读 . 例如,如果您必须分配程序开始时间,或者您必须在对象初始化时存储用户提供的值,并且您必须将其限制为进一步更改,则应使用readonly .

  • 5

    readonly 关键字用于将成员变量声明为常量,但允许在运行时计算该值 . 这与使用 const 修饰符声明的常量不同,后者必须在编译时设置其值 . 使用 readonly ,您可以在声明中或在该字段所属的对象的构造函数中设置字段的值 .

    如果您不想重新编译引用该常量的外部DLL(因为它在编译时被替换),也可以使用它 .

  • 1

    添加基本方面来回答这个问题:

    通过省略 set 运算符,可以将属性表示为只读 . 因此,在大多数情况下,您不需要将 readonly 关键字添加到属性:

    public int Foo { get; }  // a readonly property
    

    与此相反:字段需要 readonly 关键字来实现类似的效果:

    public readonly int Foo; // a readonly field
    

    因此,将字段标记为 readonly 的一个好处是可以实现与没有 set 运算符的属性类似的写保护级别 - 无需将字段更改为属性(如果出于任何原因,需要) .

  • 1

    令人惊讶的是,readonly实际上可能导致代码变慢,正如Jon Skeet在测试他的Noda Time库时发现的那样 . 在这种情况下,在只读取后,在20秒内运行的测试仅用了4秒钟 .

    https://codeblog.jonskeet.uk/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields/

  • 118

    用非常实际的方式来说:

    如果在dll中使用const,而dll B引用该const,则该const的值将被编译为dll B.如果使用该const的新值重新部署dll A,则dll B仍将使用原始值 .

    如果在read_ly中使用readonly in dll A和dll B引用,则readonly将始终在运行时查找 . 这意味着如果您为该readonly重新部署具有新值的dll A,则dll B将使用该新值 .

  • 12

    请记住,readonly仅适用于值本身,因此如果您使用引用类型readonly只保护引用不被更改 . 实例的状态不受readonly的保护 .

  • 0

    使用readonly标记的另一个有趣部分可能是保护字段不受单例初始化的影响 .

    例如,来自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会在运行时创建 .

  • 58

    存在这样的可能情况:编译器可以基于readonly关键字的存在来进行性能优化 .

    这仅适用于readonly字段也标记为静态的情况 . 在这种情况下,JIT编译器可以假设此静态字段永远不会更改 . 编译类的方法时,JIT编译器可以将此考虑在内 .

    典型示例:您的类可以具有静态只读IsDebugLoggingEnabled字段,该字段在构造函数中初始化(例如,基于配置文件) . 一旦实际的方法被JIT编译,编译器可能会在未启用调试日志记录时省略代码的整个部分 .

    我还没有检查这个优化是否实际在JIT编译器的当前版本中实现,所以这只是推测 .

  • 6

    私有只读数组要小心 . 如果暴露了这些客户端作为对象(您可以像我一样为COM互操作执行此操作)客户端可以操作数组值 . 将数组作为对象返回时,请使用Clone()方法 .

  • 1

    WPF可以带来性能优势,因为它不再需要昂贵的DependencyProperties . 这对于集合尤其有用

  • 2

    不要忘记有一种解决方法可以使用 out 参数在任何构造函数之外设置 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

  • 0

    使用 readonly 没有明显的性能优势,至少没有我完全按照您的建议完成,因为一旦初始化就会阻止修改 .

    所以's beneficial in that it helps you write more robust, more readable code. The real benefit of things like this come when you'在团队中工作或进行维护 . 将某些内容声明为 readonly 类似于在代码中为该变量的用法设置 Contract . 可以把它想象成添加文档的方式与其他关键字一样,如 internalprivate ,你're saying 287023 , and moreover you'重新执行它 .

    因此,如果您创建一个类并按设计标记某些成员变量 readonly ,那么您可以防止自己或其他团队成员稍后在他们受益的情况下犯错(以额外的语言复杂性为代价,如评论中提到的那样) ) .

  • 0

    readonly 可以在声明时初始化,也可以仅从构造函数中获取其值 . 与 const 不同,它必须被初始化并同时声明 . readonly 拥有 const 所有的东西,加上构造函数初始化

    code https://repl.it/HvRU/1

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

相关问题