> For the complete documentation index, see [llms.txt](https://postica.gitbook.io/binding-system-3/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://postica.gitbook.io/binding-system-3/overview/bind-types.md).

# Bind Types

Which type to declare, and what each one gives up.

These are the types for the [code way of binding](/binding-system-3/overview/two-ways-to-bind.md), the one you reach for when your own component should control the timing. If you are binding a field from the Inspector, none of this applies: a [proxy binding](/binding-system-3/overview/proxy-bindings.md) has no declared type at all.

`Bind<T>` is the one to reach for. The others exist because giving something up buys something back: a narrower Inspector menu, a smaller serialized footprint, or an intent that the compiler enforces.

## The family

<table><thead><tr><th width="270">Type</th><th width="120">Direction</th><th>What it is</th></tr></thead><tbody><tr><td><code>Bind&#x3C;T></code></td><td>Read, write or both</td><td>The default. Value field when unbound, full pipeline when bound, parameters, <code>ValueChanged</code> event, optional <code>UnityEvent</code>.</td></tr><tr><td><code>ReadOnlyBind&#x3C;T></code></td><td>Read</td><td>The bind path menu only offers members that can be read. Writing through it is a compile error, not a runtime surprise.</td></tr><tr><td><code>WriteOnlyBind&#x3C;T></code></td><td>Write</td><td>The menu only offers members that can be written. Unbound, the value field is a read-only display for debugging.</td></tr><tr><td><code>ReadOnlyBindLite&#x3C;T></code></td><td>Read</td><td>Like <code>ReadOnlyBind&#x3C;T></code> without parameters. The smallest serialized form, for fields you have a great many of.</td></tr><tr><td><code>BindDataFor&#x3C;T></code></td><td>Read, write or both</td><td>No value field at all. The field <em>must</em> point at something, so the Inspector never offers the unbound state.</td></tr><tr><td><code>PhasedBind&#x3C;T></code></td><td>Read, write or both</td><td><code>Bind&#x3C;T></code> with the pipeline split into stages that only re-run when their input changed. See <a href="/binding-system-3/project-tools/performance/phased-bindings.md">Phased Bindings</a>.</td></tr></tbody></table>

All of them implicitly convert to `T`, so reading is the same everywhere:

```csharp
public Bind<float> speed;
public ReadOnlyBind<Color> tint;

void Update()
{
    transform.Rotate(0, speed * Time.deltaTime, 0);   // implicit, no .Value
    _renderer.material.color = tint;                 // implicit
}
```

Going the other way needs `.Bind()` or a constructor, because a plain `T` carries no binding information:

```csharp
public Bind<float> speed = 90f.Bind();
public Bind<float> other = new Bind<float>(90f);
```

## Choosing

```
Do I need to write through this field?
├── No, only read
│   ├── Do I need method or indexer parameters?
│   │   ├── Yes  →  ReadOnlyBind<T>
│   │   └── No   →  ReadOnlyBindLite<T>
├── Only write             →  WriteOnlyBind<T>
├── Both, or not sure      →  Bind<T>
└── It must always point somewhere  →  BindDataFor<T>
```

{% hint style="success" %}
If in doubt, use `Bind<T>`. Narrowing later is a one-word change and the serialized data carries over.
{% endhint %}

## Forcing a mode without changing the type

Sometimes the field type is right but the intent is not. Attributes do that without giving up `Bind<T>`:

```csharp
public class Turret : MonoBehaviour
{
    [ReadOnlyBind]  public Bind<float> targetAngle;  // read-only menu
    [WriteOnlyBind] public Bind<bool>  isFiring;     // write-only menu
    [Bind(BindMode.Read)] public Bind<int> ammo;     // same thing, explicit
}
```

See [Attributes](/binding-system-3/reference/attributes.md) for the whole list, including `[BindType]` for generic containers and `[HideMember]` to keep a type out of the bind path menu.

## The value and the binding are separate

Every bind type serializes three things: the plain value, an `_isBound` flag, and the bind data. Turning the toggle off does not erase the binding, and turning it on does not erase the value. You can wire a binding up, switch it off to test with a hand-typed number, and switch it back on.

```csharp
public Bind<float> speed;

void Start()
{
    Debug.Log(speed.IsBound);   // is this field driven from somewhere?
    Debug.Log(speed.Value);     // the effective value, bound or not
}
```

## Noticing a change

Every type except `WriteOnlyBind<T>` raises `ValueChanged` when the value changes:

```csharp
void OnEnable()  => speed.ValueChanged += OnSpeedChanged;
void OnDisable() => speed.ValueChanged -= OnSpeedChanged;

void OnSpeedChanged(float oldValue, float newValue)
    => Debug.Log($"{oldValue} -> {newValue}");
```

### When it is raised

Most of the time the event is raised **immediately**, inside the call that changed the value. Assigning the field from your own code, a write through the binding, and a modifier pushing a value back up the chain all raise it there and then, before the assigning line returns. The old and new values are compared first, so assigning the same value again raises nothing.

```csharp
speed.Value = 12f;   // OnSpeedChanged runs here, before the next line
```

Polling is the fallback for the case where nothing in your code does the assigning. Subscribing registers the binding with the engine, which then reads it once per frame and compares. That is what notices a change made at the far end of the binding, in an object that knows nothing about you. Unsubscribing unregisters it again.

This matters most for `ReadOnlyBind<T>` and `ReadOnlyBindLite<T>`. You cannot assign a value through them at all, so there is never an assignment to raise the event from, and polling the source is the only way the change can be seen.

{% hint style="info" %}
A change noticed by polling is reported once per frame, between `Update` and `LateUpdate`, not at the instant the underlying value moved. A change you make yourself is reported at the instant you make it. If you need a value to be correct inside a specific callback, read the field there, or use a [proxy binding](/binding-system-3/overview/proxy-bindings.md) with the matching [update point](/binding-system-3/overview/modes-and-updates.md).
{% endhint %}

### Unbound fields

On `Bind<T>`, `ReadOnlyBind<T>` and `ReadOnlyBindLite<T>`, the event also fires by default for an **unbound** field whose value you assign. Turn that off per instance:

```csharp
speed.UnboundValueFiresValueChanged = false;
```

### The Inspector event

There is also a serialized `UnityEvent<T>`, off by default, switched on from the bind menu under **Settings ▸ Value Changed Event**. That one is for designers: it shows up in the Inspector and can call anything.

## Collections

Bind types work inside lists and arrays, and each element keeps its own binding. That gives you a collection whose items come from different places:

```csharp
public List<Bind<float>> mixedInputs;   // one from a slider, one constant, one from physics
```

<figure><img src="https://3048705056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLUfBXR02sJ5i0MxwYbaV%2Fuploads%2F4iSUUcus21cSv95s2Ba3%2FScreenshot%202026-09-27%20at%2022.42.37.png?alt=media&amp;token=4e684850-f4ba-4418-8295-2b6f38936d51" alt="" width="563"><figcaption><p>A list where each element is bound to a different source</p></figcaption></figure>

## Properties, not just fields

Bind attributes work on properties too, as long as Unity serializes the backing field. The common pattern is a serialized private field with a public accessor:

```csharp
[SerializeField] private Bind<float> _speed;
public float Speed => _speed;
```

## Related pages

* [Binding from Code](/binding-system-3/overview/code.md): building and rewiring bindings at runtime.
* [Attributes](/binding-system-3/reference/attributes.md): every attribute that affects a bind field.
* [Phased Bindings](/binding-system-3/project-tools/performance/phased-bindings.md): what the phased variant actually does.
