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 when it belongs to the application.
Signature#
class <Name> {
public <Type> <Field>;
public <Return> <Method>(<params>) { … } // reads/writes this instance's fields
}Description#
A method reads its own fields#
Inside a method the class's fields are in scope by name — there is no ceremony:
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#
A class is an in-memory value, so a method may change it. Nothing is persisted — there is no row behind it:
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?#
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 is for.
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?#
| 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 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#
- Method overloads — declaring several methods with one name, and how a call picks between them
- on every —
on every, for behaviour that repeats on a clock rather than waiting to be asked - constructor — building an instance with arguments
- function — behaviour that belongs to the application, not to a shape
- entity — why an entity has no methods