我正在使用C#开发一个小型Unity项目。
我有一个类UnitManager
,其中包含类Unit
的实例列表。
每当属性(例如,健康)在Unit
的实例内发生变化时,我都想提出一个事件。我也使用UnitManager
来存储eventHandlers。例如,我有一个UI管理员订阅UnitManager
中的事件。
我遇到的问题是我不希望Unit
类知道UnitManager
类。
我现在的代码(下面)有效,但我认为这会被认为是耦合代码吗?
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 /松散耦合方面)是什么?
答案 0 :(得分:2)
试试这个:
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
的每个实例并监听事件。
答案 1 :(得分:1)
是否在启动时创建了侦听器,还是可以通过依赖注入容器解析它们?
另一种减少耦合并整合附加事件处理程序的方法是使用域事件总线。
你有一个单独的EventBus
类 - 有些人使用静态类,我更喜欢使用IEventBus
接口并将事件总线注入可能引发事件的类中。
然后,您将类注册为特定类型事件的处理程序。所以你可能有一个HealthChangedEvent
类,一个IEventHandler<HealthChangedEvent>
接口,然后你注册实现该接口的类。
这是关键 - 一个类被注册为事件的类型的处理程序,因此它不必订阅可能发布事件的各个类。如果任何引发HealthChangedEvent
,那么每个注册的处理程序都会收到它。你正在注册一些听众,而不是订阅很多出版商。
当您的班级调用eventBus.Raise(healthChangedEvent)
时,事件总线会将事件传递给每个已注册的处理程序。这样,您可以拥有与发件人分离的任意数量的侦听器。他们只知道这件事。除非对事件传递了对源的引用,否则他们不知道源。
它非常适用于依赖注入容器,因为容器可以解析IEventHandler<TEvent>
。
这是创建自己的blog post。我有自己的实现,允许我使用不同的DI容器而不会耦合到任何。我会把它放到博客文章中,以便分享。
更新:Here's my own implementation。我会在某些时候将它放在存储库中以及所有单元测试。