I have a simple event bus to fire Rx events:
public class EventBus
{
private readonly LayerStack _layerStack;
private readonly ConcurrentDictionary<string, Subject<IEvent>> _subjects = new ();
public EventBus(LayerStack layerStack) => _layerStack = layerStack;
public void Publish<T>(T @event) where T : IEvent
{
foreach (var layer in _layerStack.Layers)
if (_subjects.TryGetValue(layer, out var subject))
subject.OnNext(@event);
}
public IObservable<T> AsObservable<T>() where T : IEvent => GetLayerSubject().OfType<T>().Where(e => !e.Handled);
public IDisposable Subscribe<T>(Action<T> action) where T : IEvent => GetLayerSubject().OfType<T>().Where(e => !e.Handled).Subscribe(action);
public IDisposable Subscribe<T>(IObserver<T> observer) where T : IEvent => GetLayerSubject().OfType<T>().Where(e => !e.Handled).Subscribe(observer);
private ISubject<IEvent> GetLayerSubject()
{
var layer = new StackFrame(2).GetMethod()!.DeclaringType!.Name;
if (!_subjects.ContainsKey(layer))
_subjects.AddOrUpdate(layer, new Subject<IEvent>(), (l, s) => _subjects[l] = s);
return _subjects[layer];
}
}
I am using a Stack to propagate events through layers in the stack to ensure ordering of events through system layers but just updating the Handled
property is doing nothing to the original event. Is there some way to pass these events by reference instead?
Consuming code:
public class CoreLayer // will be in first layer
{
public CoreLayer(ILogger<CoreLayer> logger, EventBus events)
{
events.Subscribe<WindowOpenedEvent>(e =>
{
e.Handled = true;
logger.LogInformation("{Event}", e.ToString()); // Should run
});
}
}
public class UILayer // will be in second layer
{
public UILayer (ILogger<UILayer> logger, EventBus events)
{
events.Subscribe<WindowOpenedEvent>(e =>
{
logger.LogInformation("{Event}", e.ToString()); // Shouldn't run
});
}
}
It turns out the issue was using structs with interfaces, I switched to classes for the events and an abstract Event class and it works as intended.