c# readonly

2023-10-25 10:52:08 浏览数 (3)

在C#中,readonly特性用于声明一个只读字段。一个只读字段可以在声明时或在构造函数内部进行初始化,一旦被赋予了初始值,它就不能被改变。

使用

下面是使用 readonly 特性的基本语法:

代码语言:javascript复制
public readonly int MyField;

您也可以在构造函数中初始化只读字段,如下所示:

代码语言:javascript复制
public class MyClass
{
    public readonly int MyField;

    public MyClass(int value)
    {
        MyField = value;
    }
}

注意事项

  • 只读字段只能在声明或者构造函数中赋值。
  • 只读字段的值不能更改,相当于常量,但常量在编译时需要初始化,只读字段则在运行时初始化。
  • 只读字段可以具有不同的值,取决于所用的构造函数来创建对象。
  • 静态只读字段在运行时初始化,其初始化发生在静态构造函数中(如果存在)或者在出现此类的任何其他静态成员之前。

注意:对于引用类型,readonly修饰符仅防止修改字段本身的值,而不是防止修改字段引用的对象。换句话说,你不能更改引用字段的指向,但是可以更改该字段指向的对象的属性或方法。

readonly修饰的字段GC如何处理它

垃圾收集器(GC)对 readonly 修饰的字段无特殊处理。只读性质并不影响对象的垃圾回收。

垃圾回收主要基于一个对象是否还被引用来决定是否进行回收。如果一个对象不再被任何其他对象引用,那么它就会被 GC 标记为可回收。当 GC 运行时,这些标记为可回收的对象将被清理掉,释放其占用的内存资源。

而对于 readonly 字段,它仅仅是限制了该字段的修改,也就是说一旦字段被初始化后,字段本身的值是不可以被改变的。然而这并不影响其所引用的对象在内存中的生命周期,也不影响垃圾回收的机制。

如果一个 readonly 字段所引用的对象不再被其他对象引用,那么这个对象同样会被标记为可回收,并在 GC 运行时被清理。

readonly修饰的字段内存分配在哪里

在C#中,readonly关键字修饰的字段的内存分配位置取决于它是否被声明为静态(static)。

  • 如果readonly字段是实例字段(非静态),那么它的内存将会在堆上分配,作为创建对象实例时分配的一部分。每个对象实例都有自己的readonly实例字段副本。
  • 如果readonly字段是静态字段,那么它的内存将会在高频堆(High Frequency Heap)上分配,此处用于存储所有的静态数据。所有实例共享一个readonly静态字段。

无论是静态还是非静态的 readonly 字段,都只能在声明时或在相应的构造函数中初始化。对于静态 readonly 字段,这通常发生在静态构造函数或者第一次引用类之前。对于非静态 readonly 字段,它们在实例构造函数中初始化。

下面是一个代码示例:

代码语言:javascript复制
public class MyClass
{
    public readonly int InstanceField;  // 在堆上分配内存
    public static readonly int StaticField;  // 在高频堆上分配内存

    public MyClass(int value)
    {
        InstanceField = value;
    }

    // 静态构造函数
    static MyClass()
    {
        StaticField = 10;
    }
}

readonly线程安全

readonly关键字在C#中表示一旦字段被初始化,它的值就不能再被改变。这种不可变性在某种程度上可以提高多线程环境下的线程安全性。

对于值类型(如intbooldouble等)或不可变的引用类型(如string),readonly字段是绝对线程安全的,因为他们的状态一旦初始化就无法改变。

但是,对于可变的引用类型(如列表、字典或自定义类),虽然你无法改变readonly字段本身引用的对象,但你仍然可以修改该对象的内部状态。例如,你可以向一个readonly的列表中添加项目。如果不同的线程试图同时修改这个列表,那么可能会遇到线程安全问题。

以下是一个例子,解释了以上的概念:

代码语言:javascript复制
public class MyClass
{
    public readonly List<int> MyList = new List<int>();  // 可变引用类型

    // ...其他代码...
    
    public void AddItem(int item)
    {
        // 需要保证线程安全,因为MyList是可变的
        lock (MyList)
        {
            MyList.Add(item);
        }
    }
}

readonly只能保证字段本身不会被改变,而不能保证其引用的对象的状态不被改变。在处理可变的引用类型时,还需要采取额外的同步措施以确保线程安全。

readonly修饰的对象传递的是引用还是实例

无论字段是否被 readonly 修饰,对象的传递方式(引用或值)都取决于其类型。

1 人点赞