如何订阅在 class 的多个实例中引发的事件?

How to subscribe to events raised in multiple instances of a class?

我正在使用 C# 开发一个小型 Unity 项目。

我有一个 class UnitManager,其中包含 class Unit 的实例列表。 每当 属性(例如健康)在 Unit 的实例中发生变化时,我想引发一个事件。我还使用 UnitManager 来存储事件处理程序。例如,我有一个 UI 经理订阅了 UnitManager.

中的事件

我遇到的问题是我不想 Unit class 知道 UnitManager class。 我现在(下面)的代码有效,但我认为这会被视为耦合代码?

public class Unit
{
    private int health;
    public int Health {
        get { return health; }
        set
        {
            health = value;
            UnitManager.Instance.OnHealthChanged(this); //Unit shouldn't call UnitManager, right?
        }
    }
}

public class UnitManager
{

    protected List<Unit> units = new List<Unit>();

    public delegate void UnitHealthChangedEventHandler(object source, UnitEventArgs args);
    public event UnitHealthChangedEventHandler HealthChanged;

    public virtual void OnHealthChanged(Unit _unit)
    {
        if (HealthChanged != null)
        {
            HealthChanged(this, new UnitEventArgs() { unit = _unit });
        }
    }
}

public class UIManager
{
    void Start()
    {
        UnitManager.Instance.HealthChanged += OnUnitHealthChanged;
    }
}

根据 MSDN sample,事件处理程序应存储在 Unit(事件发送者)中。不同的是MSDN上的示例只有一个"Counter"的实例,而我的代码有多个实例。这意味着我必须遍历 Unit 的所有实例,然后订阅所有实例。恐怕这会成为性能问题。 (特别是因为我在我的代码中的多个地方都遇到了同样的问题)

总结一下我的问题:让 EventHandler 处理在一个事件的多个实例中引发的事件的最佳方法是什么(就 OOP/loosely 耦合而言) class?

试试这个:

public class HealthChangedEventArgs
{
    public HealthChangedEventArgs(int health) { Health = health; }
    public int Health  { get; private set; } // readonly
}

public class Unit
{
  //The delegate - event handlers must have this signature
    public delegate void HealthChangedEventHandler(object sender, HealthChangedEventArgs e);

    // The event
    public event HealthChangedEventHandler HealthChangedEvent;

    //Method for raising the event - derived classes can also call it.
    protected virtual void RaiseHealthChangedEvent()
    {
        // Raise the event by using the () operator.
        if (HealthChangedEvent != null)
            HealthChangedEvent(this, new HealthChangedEventArgs(health));
    }

    private int health;
    public int Health
    {
        get { return health; }
        set
        {
            health = value;
            RaiseHealthChangedEvent();
        }
    }
}

Unit 没有在 UnitManager 上调用方法。它只是引发一个事件。它不知道什么——如果有的话——在听。

UnitManager 负责将其事件处理程序添加到 Unit 的每个实例并监听事件。

侦听器是在启动时创建的还是可以通过依赖注入容器解析?

另一种减少耦合并整合附加事件处理程序的方法是使用域事件总线。

你有一个单例 EventBus class - 有些使用静态 class,我更喜欢使用 IEventBus 接口并将事件总线注入 class 可能引发事件。

然后您将 classes 注册为特定类型事件的处理程序。因此,您可能有一个 HealthChangedEvent class、一个 IEventHandler<HealthChangedEvent> 接口,然后您注册了 class 实现该接口的实体。

这是关键 - class 被注册为 type 事件的处理程序,因此它不必订阅单个 classes 可能会发布事件。如果 anything 引发 HealthChangedEvent 那么每个注册的处理程序都会收到它。您正在注册一些听众,而不是订阅很多发布者。

当您的 class 调用 eventBus.Raise(healthChangedEvent) 时,事件总线将事件传递给每个已注册的处理程序。这样你就可以拥有任意数量的与发送者分离的监听器。他们只知道事件。除非随事件传递对源的引用,否则他们不知道源。

它特别适用于依赖注入容器,因为容器可以解析 IEventHandler<TEvent>.

的实例

这里是 blog post 关于创建您自己的。我有自己的实现,允许我使用不同的 DI 容器而不与任何容器耦合。我会把它放到博客中 post 以便于分享。


更新:Here's my own implementation。我会在某个时候将它与所有单元测试一起放入存储库中。