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

Reference / Class

Classes

A class is an in-memory shape — data plus the behaviour that belongs to it — and it never touches the database. That is the whole distinction from an entity: an entity is a table, a class is something you build, pass, and compute with in a function or a component. Classes have a constructor and methods, exactly as in C#, and like a C# class they are REFERENCE types: `var b = a;` aliases rather than copies, and `list[i].Field = x` sticks.

stable6 examples compiled by CIclassguide

Summary#

A class is an in-memory shape: fields and the behaviour that belongs to them. It is not persisted and has no table — that is the one line that separates it from an entity. Like a C# class it is a reference type ([[class-index#reference|what that means for assignment]]). Reach for a class when you need a structured value to compute with inside a function or a component: a parsed request, a calculation's intermediate, a projection's target, a small bundle of related fields you pass around.

If you're deciding between the two: does it need to be stored and queried? Yes → an entity. No → a class.

Description#

A value, not a row#

An entity lives in the database; the platform loads it, tracks your edits, and commits them. A class does none of that — you new it, read and write its fields, hand it to another function, and it vanishes when the work is done. No schema, no migration, no security rules: it is just a value in memory, like any C# object. It carries its own constructor and methods:

class Money {
  public decimal Amount;
  public string Currency;

  // the ctor assigns the required Currency, so `new Money(9.5m, "USD")` needs no initializer — the compiler infers it
  public Money(decimal amount, string currency) {
    Amount = amount;
    Currency = currency;
  }

  public string Label() => Amount.ToString("F2") + " " + Currency;
}

Build one and read its label — the constructor runs, then the method runs on the value:

[Test]
void Builds_And_Labels() {
  var m = new Money(9.5m, "USD");
  Assert.Equal("9.50 USD", m.Label());
}

Does var b = a; copy it, or point at it?#

It points at it. A class is a REFERENCE type, exactly like a C# class — "value" above describes what a class is for (a shape you compute with, with no table behind it), never how assignment behaves. Three consequences, and all three are the C# ones:

  • var b = a; makes b another name for the same object. A write through b is visible through a.
  • list[i].Field = x sticks — the indexer hands back the object, not a copy of it, so you can edit rows in place.
  • A list you filtered holds the same objects as the list you filtered it from, so editing through one is visible through the other.

In a component, that in-place write also RE-RENDERS — a render slot reading a class field is tracked like any other read, so you never need to reassign the list to make the screen move. What you cannot do is point a control's two-way value: at a class field. Both halves, with compiled proof: [[ui-reactivity#class-values]].

class Ticket {
  public string Code;
  public decimal Price;
  public Ticket(string code, decimal price) { Code = code; Price = price; }
}
[Test]
void A_Class_Is_A_Reference() {
  var a = new Ticket("T1", 10m);
  var b = a;
  b.Price = 99m;
  Assert.Equal(99m, a.Price);            // same object — `b` was never a copy

  var rows = new List<Ticket>();
  rows.Add(new Ticket("T2", 1m));
  rows.Add(new Ticket("T3", 2m));
  rows[0].Price = 42m;
  Assert.Equal(42m, rows[0].Price);      // an edit through the indexer sticks

  var dear = rows.Where(t => t.Price > 1m).ToList();
  dear[0].Price = 50m;
  Assert.Equal(50m, rows[0].Price);      // the filtered list holds the SAME objects

  var missing = rows.FirstOrDefault(t => t.Code == "nope");
  Assert.True(missing == null);          // no match is null, not an empty Ticket
}

with is the one place a copy happens, and that is the point of it: a with { Price = 5m } builds a new object and leaves a alone. It is opt-in copying, not evidence that assignment copies.

It has a constructor#

A class declares one constructor — its name is the class name, it takes no return type, and it runs when you write new T(args). The constructor body runs first; object-initializer syntax ({ Member = value }) applies after it. Use the constructor for the setup a valid instance always needs.

It has methods#

Behaviour that belongs to a shape lives on the shape. A method has a receiver and is called as value.Method(...) — the natural home for logic that is about this value rather than about the database. A method body resolves function-style with the class as its receiver, the same mechanism a component's action uses.

It has properties#

A property reads and writes like a field but runs a body on access — a value computed from other fields (Total => Qty * Price), a setter that validates a write, or an auto-property ({ get; set; }) whose storage the platform synthesizes. It is a field-shaped pair of methods, so it works everywhere a method does, the browser included.

Its fields can have defaults#

A field can carry an initializer — decimal Rate = 0.25m;. It runs at construction, before the constructor body and before any object initializer, exactly as in C#: the constructor (and every read) sees the declared value unless something later overwrites it.

class Cart {
  public decimal Rate = 0.25m;
}
[Test]
void Field_Default_Applies() {
  var c = new Cart();
  Assert.Equal(0.25m, c.Rate);          // the declared default, applied at construction
  var d = new Cart { Rate = 0.1m };
  Assert.Equal(0.1m, d.Rate);           // an object initializer overrides it
}

Visibility follows C##

A top-level class is internal by default (an entity or enum is public) — see type visibility (public / internal). Mark it public when code in another namespace needs to name it.

See also#

Related

constructor

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

class methods

Behaviour attached to a class — a method with a receiver, called as value.Method(). Classes are in-memory values, so a…

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…

`readonly` fields

A `readonly` field can be assigned only where it is declared or in a constructor of the class that declares it…

Copying a class with changes

`with` makes a COPY of a class value and replaces the fields you name. Everything you do not name comes from the value…

Method overloads

A class can declare several methods with the same name, as long as they differ in their parameter types. Each call…

`static` methods

A `static` method belongs to the type rather than to any instance, and is called on the type name — `Money.Round(x)`…

`params` parameters

`params` lets a method be called with any number of trailing arguments — `Total(1m, 2m, 3m)` — which arrive as one…

Sequence fields on a class

A class can hold many values in one field — `string[]`, `int[]`, `List<Tag>`, `HashSet<string>`, `Dictionary<string…

Class inheritance

A class can derive from another class with `class Circle : Shape`, inheriting its fields and its methods to any depth…

Testing which class a value is

`s is Circle` asks which type a value actually is at run time, answering by the value's own type rather than the type…

entity

Declares a persisted type — a table of rows the app stores, queries and secures. Every entity gets an Id and audit…

type visibility (public / internal)

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

Functions (the unit of work)

A function is where your app's logic lives — a top-level unit of work, written like a C# method, that runs on the…