State
Create, Read, And Write State
Create AzState<T>, read current values, and write accepted source values.
- Initial values
- Comparers
- Value
- SourceSet
This page covers the basic AzState<T> shape: when to use it, how to create it, how to read it, and how to write with SourceSet.
What AzState Is
AzState<T> is a reactive property.
Use it when you have a value that changes over time and other code should react when that value changes.
using Azkar.Eda;
using UnityEngine;
public sealed class PlayerModel
{
public readonly AzState<int> Health = new(100);
public readonly AzState<int> Score = new(0);
public readonly AzState<bool> IsMuted = new(false);
}The most important operation is SourceSet.
Health.SourceSet(90);If the value changed, subscribers are notified. If the value is the same, subscribers are not notified unless you force it.
Health.SourceSet(90); // notifies if previous value was not 90
Health.SourceSet(90); // no notification
Health.SourceSet(90, force: true); // notification even though unchangedWhen to Use AzState
Use AzState<T> for mutable values:
- health
- score
- selected item
- current weapon
- volume
- settings toggles
- UI field values
- model/view-model values
- cached derived status text
Use AzEvent or AzPriorityEvent for discrete events:
- jump pressed
- damage happened
- projectile spawned
- item collected
- save completed
- round started
The difference is simple:
- "This value currently is X."
AzState<T>- "A discrete event occurred."
AzEvent- "A discrete event occurred, and ordered phases matter."
AzPriorityEvent
Core Mental Model
An AzState<T> owns:
- a current value
- a version number
- an equality comparer
- optional validation
- a notification schedule
- a subscriber list
Subscribers receive the current value when the state changes.
private readonly AzEventTokenBag _bindings = new();
private void OnEnable()
{
Player.Health.Subscribe(OnHealthChanged).AddTo(_bindings);
}
private void OnDisable()
{
_bindings.DisposeAll();
}
private void OnHealthChanged(int health)
{
Debug.Log($"Health is now {health}");
}Every subscription returns an AzEventToken. Store that token or add it to an AzEventTokenBag.
Creating State
With an Initial Value
public readonly AzState<int> Health = new(100);
public readonly AzState<float> Volume = new(0.75f);
public readonly AzState<string> Status = new("Ready");When a state has an initial value:
HasValueistrueValuereturns that valueSubscribe(..., notifyImmediately: true)invokes immediately with that value
Without an Initial Value
public readonly AzState<string> SelectedSaveSlot = new();When a state has no value yet:
HasValueisfalseSubscribe(..., notifyImmediately: true)does not call the subscriber until a value is set- the first successful
SourceSetestablishes the value
With a Custom Comparer
AzState<T> uses EqualityComparer<T?>.Default unless you provide a comparer.
For reference types, default equality is often reference equality unless the type overrides equality.
public sealed class InventorySnapshot
{
public int ItemCount;
}
public sealed class InventorySnapshotComparer : IEqualityComparer<InventorySnapshot?>
{
public bool Equals(InventorySnapshot? x, InventorySnapshot? y)
=> x?.ItemCount == y?.ItemCount;
public int GetHashCode(InventorySnapshot? obj)
=> obj?.ItemCount.GetHashCode() ?? 0;
}
public readonly AzState<InventorySnapshot> Inventory =
new(new InventorySnapshotComparer());Use a custom comparer when you want "same content" to mean "no state change."
Reading State
int health = Health.Value;
bool hasHealth = Health.HasValue;
int version = Health.Version;
AzStateSchedule schedule = Health.Schedule;Version increments when the state accepts a changed or forced value.
ValueSource is a property wrapper around SourceSet:
Health.ValueSource = 80;Most code should prefer explicit SourceSet(...) because it makes mutation visible.
Writing State with SourceSet
bool changed = Health.SourceSet(80);SourceSet returns:
trueon the main thread when the value changed or was forcedfalseon the main thread when the value was unchanged or rejected by strict validationtrueoff-thread when the write request was accepted for later main-thread application
Force Notification
Health.SourceSet(Health.Value, force: true);Use this when the value is the same but bindings need to reapply, such as after UI reconstruction.
Refresh
Refresh() notifies subscribers with the current value without changing it.
Health.Refresh();Refresh():
- does nothing if
HasValueis false - increments
Version - skips validation
- follows the state's schedule
Use it when a view needs to re-read current state because external presentation context changed.
