Global scope
GlobalScope is Saneject's static runtime registry for globally accessible Component instances.
Saneject is mostly editor-time DI, but some runtime scenarios still need lookup by type. GlobalScope and RuntimeProxy covers that gap:
BindGlobal<TComponent>()stores resolved components in aScopeat editor-time and registers them at runtime.RuntimeProxycan resolve throughFromGlobalScope()for fast lookup.- You can also call
GlobalScopedirectly as a service locator when needed.
ℹ️
GlobalScopeis automatically cleared when returning to Edit Mode after Play Mode ends. This prevents stale global registrations from leaking into the next Play session when domain reload is disabled.
What problem it solves
Unity serialization cannot serialize direct references between certain boundaries:
- Scene ↔ other scene
- Scene ↔ prefab asset
- Prefab asset ↔ other prefab asset
GlobalScope gives you a shared runtime lookup point for registered components. You declare those components in a normal Scope, then Saneject registers them GlobalScope before regular Awake() methods run.
This keeps the common workflow explicit:
- Resolve dependency candidates in the editor through bindings.
- Persist global candidates on the declaring
Scope. - Register those components into
GlobalScopeat runtime startup.
Declaring global components with bindings
Use BindGlobal<TConcrete>() inside DeclareBindings():
using Plugins.Saneject.Runtime.Scopes;
public class BootstrapScope : Scope
{
protected override void DeclareBindings()
{
BindGlobal<AudioManager>()
.FromScopeSelf();
}
}
BindGlobal<T>():
- Only accepts
Componenttypes. - Supports normal
From...locator methods andWhere...filters. - Does not support qualifiers (
ToID,ToTarget,ToMember). - Does not support runtime proxy binding methods.
See Binding and Scope for more information.
Global lifecycle
When you declare BindGlobal<T>(), Saneject handles it in two phases.
Editor phase:
- During injection, Saneject resolves the binding like any other component locator.
- If multiple candidates are found, the first candidate is selected.
- The selected component is serialized into the declaring
Scope's global components list.
Runtime phase:
Scope.Awake()runs at execution order-10000.- The scope registers serialized global components into
GlobalScope. - The same
Awake()then swaps runtime proxies to real instances. Scope.OnDestroy()unregisters the components that this scope owns.
That order matters: global registration runs before proxy swapping, so runtime proxies configured to resolve from GlobalScope can do that during startup.
For full proxy behavior, see Runtime proxy.
Using global scope with runtime proxy
A common pattern is:
- In one scope, declare a global component with
BindGlobal<TConcrete>(). - In another scope or context, bind an interface through
FromRuntimeProxy().FromGlobalScope().
using Plugins.Saneject.Runtime.Scopes;
public class BootstrapScope : Scope
{
protected override void DeclareBindings()
{
BindGlobal<GameManager>()
.FromScopeSelf();
}
}
using Plugins.Saneject.Runtime.Scopes;
public class HudScope : Scope
{
protected override void DeclareBindings()
{
BindComponent<IGameManager, GameManager>()
.FromRuntimeProxy()
.FromGlobalScope();
}
}
This is usually the intended use of GlobalScope: fast runtime lookup for proxy resolution, not a replacement for normal editor-time injection.
For full proxy behavior, see Runtime proxy.
Using global scope directly as a service locator
If you want to avoid runtime proxies, direct usage is available when you need fast runtime lookup outside the injection pipeline:
using Plugins.Saneject.Runtime.Scopes;
using UnityEngine;
public class AudioBootstrap : MonoBehaviour
{
private void Start()
{
if (GlobalScope.TryGetComponent<AudioManager>(out AudioManager audio))
audio.InitializeMixer();
}
}
API overview:
GlobalScope.GetComponent<T>()GlobalScope.TryGetComponent<T>(out T component)GlobalScope.IsRegistered<T>()GlobalScope.RegisterComponent(Component instance, Object caller)GlobalScope.UnregisterComponent(Component instance, Object caller)GlobalScope.Clear()
Use manual RegisterComponent and UnregisterComponent carefully. Most projects should let Scope manage lifecycle through BindGlobal<T>().
Rules and constraints
GlobalScopestoresComponentinstances only.- Only one registration per concrete component type is allowed at a time.
- Lookup is exact-type by key. If
AudioManageris registered,GetComponent<MonoBehaviour>()does not return it. - Generic lookup methods require
T : Component, so interface lookups are not supported. - Global bindings are validated for unique type ownership. Duplicate
BindGlobal<SameType>()bindings are invalid. - Global binding candidate selection still obeys context isolation rules during editor resolution.
- Global bindings are not used to resolve
[Inject]fields or methods directly. They feed runtime registration, not normal binding resolution. - Modifying
GlobalScope(Register,Unregister,Clear) is runtime-only. Calls outside Play Mode are rejected. - Unregister requires ownership: only the same caller object that registered a type can unregister that type.