Skip to content

FAQ -- Frequently Asked Questions

Back to Index | Troubleshooting | Getting Started | Glossary


Do I need to use attributes or source generators

  • No. You can implement IUntargetedMessage<T>, ITargetedMessage<T>, or IBroadcastMessage<T> directly (recommended for structs). Attributes are optional and help tooling/source-gen.

Do I need an assembly definition for the source generator to work?

  • No. The source generator and analyzers run for code in Unity's default Assembly-CSharp and in your own assembly definitions alike, so you do not need to add an .asmdef just to get generation. Mark the message type partial and apply a [DxUntargetedMessage] / [DxTargetedMessage] / [DxBroadcastMessage] attribute (or implement the matching I*Message interface). If generated members are missing, see Troubleshooting.

Which message type should I use?

  • Untargeted - global notifications (any listener).
  • Targeted - commands/events for a specific recipient.
  • Broadcast - facts emitted from a source that others may observe.

Does DxMessaging allocate memory? Is dispatch zero-GC?

  • Steady-state dispatch is allocation-free: emitting a struct message and invoking a registered handler allocates nothing after the first warm-up emit of that message type (a one-time JIT cost in the editor that IL2CPP precompiles away). The by-ref (FastHandler) handler overloads also avoid copying the struct on each call; the by-value Action<T> overloads add one struct copy per dispatch but still do not allocate. Heap allocations come from constructing a class message instance; boxing happens only if you upcast a struct message to a non-generic interface yourself. Registration itself allocates a small, bounded amount per handler (delegates and dictionary entries), so register handlers in Awake/setup rather than every frame. See Performance for measured numbers and Troubleshooting if you observe unexpected allocations.

How do I enforce ordering?

  • Use the priority parameter at registration; lower runs earlier. Interceptors run before handlers; post-processors run after.

Can I observe all targets/sources for a type?

  • Yes. Use RegisterTargetedWithoutTargeting<T> or RegisterBroadcastWithoutSource<T> (and their post-processor counterparts).

How do I diagnose what's happening?

My MessageAwareComponent subclass does not receive messages. What is wrong?

The most common cause is forgetting to call base.Awake() (or base.OnEnable(), base.OnDisable(), base.OnDestroy(), base.RegisterMessageHandlers()) when you override one of those methods. The framework's setup runs in those base calls; without them, your registration token is never created or your handlers never enable. The Roslyn analyzer flags this as DXMSG006. See Inheritance and base calls for the full list of guarded methods.

What happens if I register a listener inside a message handler?

  • The newly registered listener will not run for the current message emission. It will only become active starting with the next message emission.
  • This is called "snapshot semantics" -- when a message is emitted, DxMessaging takes a snapshot of all current listeners and uses that frozen list for the entire emission.
  • This applies to all listener types (handlers, interceptors, post-processors) and all message categories (Untargeted, Targeted, Broadcast).
  • This behavior prevents infinite loops and ensures predictable execution order. See Interceptors & Ordering for details and examples.

Do I need a global bus?

  • A global bus is provided (MessageHandler.MessageBus). You can also create and pass your own MessageBus instance to isolate subsystems and tests.

Can I use DxMessaging with a dependency injection container?

  • Yes. Installers ship for VContainer, Zenject, and Reflex; see Integrations. They wire the message bus into the container so your message-aware types resolve and register through it instead of reaching for the global bus.

How do I unit-test code that sends or receives messages?

  • Construct a dedicated MessageBus per test and pass it to the participants instead of using the global bus, so registrations cannot leak between tests. Emit a message, then assert your handler observed it. Isolating the bus also lets tests run in parallel without cross-talk.

Is this compatible with Unity's SendMessage/UnityEvents

  • Yes. You can integrate with legacy patterns via ReflexiveMessage. Prefer DxMessaging for new code.

Why is my game retaining memory across scenes?

  • Each scene introduces new InstanceIds and sometimes new message types, which add empty slots on the bus when their handlers tear down. Idle eviction will reclaim them eventually; for deterministic cleanup call MessageHandler.TrimAll(force: true) on scene unload (or bus.Trim(force: true) for a non-global bus). See the Memory Reclamation guide for the full pattern.