Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Class

`readonly` fields

readonly <Type> <Name>; readonly <Type> <Name> = <value>;

A `readonly` field can be assigned only where it is declared or in a constructor of the class that declares it. Everywhere else — a method of the same class, a subclass constructor, an object initializer, any code holding the value — a write is a compile error. The field itself is ordinary: it holds a per-instance value chosen at construction, unlike a `const`, whose value is fixed when the code is compiled.

stable4 examples compiled by CIclassfieldsimmutability

Summary#

readonly marks a field that only the constructor may set:

class Booking {
  public readonly string Reference;
  public readonly int Seats;

  public Booking(string reference, int seats) {
    Reference = reference;
    Seats = seats;
  }
}

Once the object exists, the field is settled — every other write is refused at compile time.

Signature#

readonly T Name;                // set by a constructor
readonly T Name = value;        // set at the declaration
public readonly T Name;         // combines with any visibility

Description#

The two places a readonly field may be assigned#

There are exactly two, and they are the same two as in C#:

  1. its own declarationpublic readonly decimal Rate = 0.25m;
  2. a constructor of the class that declares it
class Invoice {
  public readonly decimal Rate = 0.25m;      // 1. at the declaration
  public readonly string Number;

  public Invoice(string number) {
    Number = number;                          // 2. in the constructor
  }
}

Both values are ordinary per-instance data — read them like any other field:

[Test]
void ReadOnly_Fields_Hold_Their_Values() {
  var i = new Invoice("INV-1");
  Assert.Equal("INV-1", i.Number);
  Assert.Equal(0.25m, i.Rate);
}

Assigning it anywhere else is a compile error#

The refusals are the feature. None of these compile:

var i = new Invoice("INV-1");
i.Number = "INV-2";                  // ✗ a write from outside
i.Rate += 0.1m;                      // ✗ `+=` is a write too

var j = new Invoice { Number = "x" };   // ✗ an object initializer runs after the constructor

class Invoice {
  public void Renumber(string n) {
    Number = n;                      // ✗ a METHOD of the same class — only a constructor may write
  }
}

The last one is worth pausing on: readonly narrows the write to constructors, not to the class. A method of the declaring class is refused exactly like outside code.

A subclass constructor cannot write the base's field#

A field belongs to the class that declares it. By the time a derived constructor's body runs, the base has already been constructed and its readonly fields are settled — so a subclass may write its own readonly fields and not its base's:

class Badge {
  public readonly string Tag;
  public Badge(string tag) { Tag = tag; }
}

class Ranked : Badge {
  public readonly decimal Rank;

  public Ranked(string tag, decimal rank) : base(tag) {
    Rank = rank;                     // its own — fine
  }
}

Writing Tag = "x" inside Ranked's constructor is a compile error: pass the value to base(…) instead, which is what the example does.

readonly is not const#

They read similarly and mean different things:

constreadonly
when the value is chosenwhen the code is compiledwhen the object is constructed
can it differ per instanceno — there is one valueyes — each new may pass a different one
what it may be initialized witha compile-time constantany expression the constructor can evaluate
is there a per-instance slotno, uses are replaced by the valueyes, it is a real field

Reach for const for a fixed number or name the whole program shares, and readonly for a value each instance is given once and then keeps. Writing both on one field is a compile error — a const has no instance slot to protect.

readonly applies to a field, not a property#

A property has no storage of its own, so there is nothing for readonly to narrow. The property spellings that mean the same things are:

  • { get; } — an auto-property only a constructor may set
  • { get; init; } — settable during construction, including from an object initializer
public readonly decimal Area { get; set; }   // ✗ readonly applies to a FIELD
public decimal Area { get; }                 // ✓ only the constructor sets it
public decimal Area { get; init; }           // ✓ an object initializer may too

Can an entity field be readonly?#

readonly is a class modifier. An entity's fields are stored rows written by data operations, and what may write them is declared in its security { } block rather than by a member modifier.

See also#

Related

class properties

A class member that reads and writes like a field but runs a body on access — a computed value, a validating setter, or…

constructor

A class declares one constructor — its name is the class name, it takes no return type, and it runs when you write new…

Classes

A class is an in-memory shape — data plus the behaviour that belongs to it — and it never touches the database. That is…

type visibility (public / internal)

A top-level type carries a public or internal visibility that decides whether code outside its namespace can name it…