C#中的readonly简要介绍

517 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第26天,点击查看活动详情

readonly修饰的属性或字段,只能在构造函数或创建对象时赋值。

readonly 的适用范围

readonly 修饰符可以用在以下4个上下文中:

  • 字段声明中,readonly 表示只能在声明时或同一个类的构造函数中赋值。

由于值类型直接包含它们的数据,readonly值类型的字段是不可变的。

但是,引用类型包含对数据的引用,readonly引用类型必须始终引用相同的对象,该对象不是不可变的,readonly修饰符阻止字段被不同的引用类型实例所替换,但不能阻止字段的实例数据被修改。

  • readonly struct 类型定义中,readonly指示该结构类型是不可变的。

  • 结构类型内的实例成员声明中,readonly指示该实例成员不会修改结构体的状态。如果该成员直接修改结构体的状态或者访问未使用readonly修饰的成员,则会报错。

  • 在 ref readonly method return 中,只读修饰符指示该方法返回引用类型,并且不允许修改该引用。

在 C# 7.2 中添加了 readonly struct 和 ref readonly 上下文,在 C# 8.0 中添加了 readonly 结构成员。

readonly 修饰引用类型,可防止该引用类型变量被重新赋值,但是不能防止引用类型实例内部的数据被修改。即防止引用的地址被修改(引用本身(相当于指针)不可改变),但地址里面的数据可以修改。

readonly 字段

在下面的示例中,字段_year不允许在ChangeYear方法内被修改

class Age
{
    private readonly int _year;
    Age(int year)
    {
        _year = year;
    }
    void ChangeYear()
    {
        //_year = 1967; // Compile error if uncommented.
    }
}

只能在以下情况下为 readonly 字段赋值:

  • 变量声明的初始化时。
  • 包含实例字段声明的实例构造函数中
  • 包含静态字段声明的静态构造函数中

readonly修饰的自动属性不能有set访问器,可以有init初始化访问器。

readonly函数访问未标记为readonly的成员时,会发出创建防御性副本的警告。

readonly 与 const 的区别

const 字段只能在声明字段时被初始化;

readonly 字段可以在字段声明和构造器中赋值多次。因此,取决于构造器的使用,readonly 字段可以有不同的值。

因此,const 字段可以看做是编译时常量或静态常量,readonly 字段可以用做运行时常量或动态常量。

对于引用类型,应该使用 readonly,这是因为引用类型是动态分配内存,不可能在编译阶段就确定它的地址,这也是和值类型(包括string)完全不一样的。

能使用 const 修饰符的需要是常量,即 基元类型,枚举类型或者字符串类型,而不能是class、struct等类型。

const 关键字声明的字段本身就是 static 静态的,只能通过 类名 对 const 常量进行访问。

class Person
{
   public const int age = 10;
}
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(Person.age);
    }
}

conststatic readonly 基本相似,但是在初始化方面,static readonly 还可以通过静态构造函数进行赋值。const 在程序编译期间获取字段的值,而 static readonly 是在程序运行时获取字段的值。

public class Person
{
    public static readonly int age = 10;
    static Person()
    {
        age = 12;
    }
}
class Program
{
   static void Main(string[] args)
   {
       Person Person = new Person();
       Console.WriteLine(Person.age);
   }
}

静态常量(Const)是指编译器在编译时候会对常量进行解析,并将常量的值替换成初始化的那个值。

动态常量(Readonly)的值则是在运行的那一刻才获得的,编译器编译期间将其标示为只读常量,而不用常量的值代替,这样动态常量不必在声明的时候就初始化,而可以延迟到构造函数中初始化。

Const的变量是嵌入在IL代码中,编译时就加载好,不依赖外部dll(这也是为什么不能在构造方法中赋值),只能在声明初始化时赋值。Const在程序集更新时容易产生版本不一致的情况。

Readonly的变量是在运行时加载,需请求加载dll,每次都获取最新的值。

Readonly修饰引用类型,引用本身不可以改变,但是引用所指向的实例的值是可以改变的。

在声明初始化、构造方法中,可以对 Readonly 变量多次赋值。

ref readonly return

ref return 的 readonly 修饰符指示返回的引用不能被修改。

下面的示例返回一个对origin的引用,使用readonly修饰符指示调用者不能修改origin:

private static readonly SamplePoint s_origin = new SamplePoint(0, 0, 0);
public static ref readonly SamplePoint Origin => ref s_origin;

参考