# class methods

> Behaviour attached to a class — a method with a receiver, called as value.Method(). Classes are in-memory values, so a method is the natural place for logic that belongs to a shape rather than to the database.

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

## Summary        {#summary}
A `class` may declare **methods**, called on a value: `cart.Total()`. A method has a receiver — the instance it was
called on — and can read and write that instance's fields.

Use a method when the logic belongs to the *shape*. Use a [function](https://osysharp.com/reference/function/declaration/) when it belongs to the
application.

## Signature      {#signature}
```osy syntax
class <Name> {
  public <Type> <Field>;
  public <Return> <Method>(<params>) { … }   // reads/writes this instance's fields
}
```

## Description    {#description}

### A method reads its own fields   {#fields}
Inside a method the class's fields are in scope by name — there is no ceremony:

```osy title="a class that can total itself" test app=class-methods
class Line {
  public string Sku;
  public int Qty;
  public decimal UnitPrice;

  public decimal Total() {
    return Qty * UnitPrice;
  }
}

decimal LineTotal(string sku, int qty, decimal price) {
  var line = new Line { Sku = sku, Qty = qty, UnitPrice = price };
  return line.Total();
}
```

### A method can mutate the instance   {#mutation}
A class is an in-memory value, so a method may change it. Nothing is persisted — there is no row behind it:

```osy title="a method that changes the value" test app=class-methods
class Basket {
  public decimal Total;
  public int Count;

  public void Add(decimal amount) {
    Total = Total + amount;
    Count = Count + 1;
  }

  public decimal Average() {
    return Count == 0 ? 0m : Math.Round(Total / Count, 2);
  }
}

decimal AverageOfThree(decimal a, decimal b, decimal c) {
  var basket = new Basket { };
  basket.Add(a);
  basket.Add(b);
  basket.Add(c);
  return basket.Average();
}
```

### Can a component hold one as state?   {#component-state}
A component field can hold a class instance, and its methods are callable from the component's actions and lifecycle
hooks like any other value. This is how a small piece of behaviour — a gate, a counter, a tiny state machine — lives
beside the page that uses it rather than being spread across loose fields.

The worked example is a **cooldown**: "don't do this again until N has passed", which has no cadence of its own and so
is not what [on every](https://osysharp.com/reference/ui/cadence/) is for.

```osy title="a cooldown gate, held as component state" test app=class-methods-component
class Cooldown {
  public DateTime ReadyAt;

  /// True once the wait has passed — then `Arm` starts the next one.
  public bool Ready() { return DateTime.UtcNow >= ReadyAt; }
  public void Arm(TimeSpan wait) { ReadyAt = DateTime.UtcNow + wait; }
}

[Page("/cooldown")]
[AllowAnonymous]
component Repeater() {
  // Ready immediately — a field is required by default, so it is given a value at the create site.
  Cooldown gate = new Cooldown { ReadyAt = DateTime.UtcNow };
  int fired = 0;

  action Nudge() {
    // Held down, this fires at most once every 90ms rather than once per event.
    if (gate.Ready()) {
      fired = fired + 1;
      gate.Arm(TimeSpan.FromMilliseconds(90));
    }
  }

  render {
    Stack(gap: 2) {
      Text($"fired {fired}");
      Pressable("nudge", onClick: Nudge);
    }
  }
}
```

Reading a field (`gate.ReadyAt`), assigning one (`gate.ReadyAt = …`) and calling a method (`gate.Ready()`) all work on
such a field. The instance is ordinary component state: it lives as long as the component does, and it is not
persisted.

### Method or function?   {#method-or-function}
| | class method | top-level function |
|---|---|---|
| Has a receiver | **yes** — the instance it is called on | no |
| Called as | `value.Method()` | `Method(value)` |
| Can write rows | no — a class has no table | **yes** |
| Good for | logic that belongs to a shape | logic that belongs to the app |

An [entity](https://osysharp.com/reference/entity/declaration/) cannot have methods: an entity body holds data, and the behaviour that acts on it is
a top-level function. That split is deliberate — it keeps the thing that is persisted separate from the thing that is
merely computed.

## See also       {#see-also}
- [Method overloads](https://osysharp.com/reference/class/overloads/) — declaring several methods with one name, and how a call picks between them
- [on every](https://osysharp.com/reference/ui/cadence/) — `on every`, for behaviour that repeats on a clock rather than waiting to be asked
- [constructor](https://osysharp.com/reference/class/constructors/) — building an instance with arguments
- [function](https://osysharp.com/reference/function/declaration/) — behaviour that belongs to the application, not to a shape
- [entity](https://osysharp.com/reference/entity/declaration/) — why an entity has no methods
