# 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.

<!-- id: class-index · area: class · stability: stable · html: https://osysharp.com/reference/class/index/ -->

## Summary        {#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](https://osysharp.com/reference/entity/declaration/). 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    {#description}

### A value, not a row   {#vs-entity}
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](https://osysharp.com/reference/class/constructors/) and [methods](https://osysharp.com/reference/class/methods/):

```osy title="a value with a constructor and a method" test app=class-index
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:

```osy title="build one and read its label" run app=class-index
[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?   {#reference}
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]].

```osy title="assignment aliases, and an edit through an index sticks" test app=class-index
class Ticket {
  public string Code;
  public decimal Price;
  public Ticket(string code, decimal price) { Code = code; Price = price; }
}
```

```osy title="the three things people write around when they assume a copy" run app=class-index
[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`](https://osysharp.com/reference/class/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   {#constructor}
A class declares one [constructor](https://osysharp.com/reference/class/constructors/) — 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   {#methods}
Behaviour that belongs to a shape lives on the shape. A [method](https://osysharp.com/reference/class/methods/) 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   {#properties}
A [property](https://osysharp.com/reference/class/properties/) 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   {#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.

```osy title="a field with a default" test app=class-index
class Cart {
  public decimal Rate = 0.25m;
}
```

```osy title="the default is there before you touch it" run app=class-index
[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#   {#visibility}
A top-level `class` is `internal` by default (an entity or enum is `public`) — see [type visibility (public / internal)](https://osysharp.com/reference/types/visibility/). Mark it
`public` when code in another namespace needs to name it.

## See also       {#see-also}
- [constructor](https://osysharp.com/reference/class/constructors/) — the single constructor and how it composes with object initializers
- [Class inheritance](https://osysharp.com/reference/class/inheritance/) — deriving one class from another, and `sealed`
- [Interfaces — a contract several types can satisfy](https://osysharp.com/reference/class/interfaces/) — a contract several types satisfy, and calling through it
- [Testing which class a value is](https://osysharp.com/reference/class/type-tests/) — asking which type a value actually is
- [class methods](https://osysharp.com/reference/class/methods/) — behaviour with a receiver, called `value.Method(…)`
- [class properties](https://osysharp.com/reference/class/properties/) — members that read/write like a field but run a body on access
- [`readonly` fields](https://osysharp.com/reference/class/readonly/) — a field only the constructor may set
- [Copying a class with changes](https://osysharp.com/reference/class/with/) — copying a value and replacing some of its fields
- [Method overloads](https://osysharp.com/reference/class/overloads/) — several methods sharing a name, told apart by their parameters
- [`static` methods](https://osysharp.com/reference/class/static/) — a method that belongs to the type rather than to an instance
- [`params` parameters](https://osysharp.com/reference/class/params/) — a method callable with any number of trailing arguments
- [entity](https://osysharp.com/reference/entity/declaration/) — the persisted counterpart, when the shape needs a table
- [type visibility (public / internal)](https://osysharp.com/reference/types/visibility/) — why a class defaults to `internal`
