如何订阅在 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。我会在某个时候将它与所有单元测试一起放入存储库中。
我正在使用 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。我会在某个时候将它与所有单元测试一起放入存储库中。