在某些情况下,我非常喜欢静态事件,但事实上我很少在其他人的代码中看到它们,这让我想知道我是否遗漏了一些重要的东西。我在这个网站上发现了很多关于静态事件的讨论,但是大多数都讨论了我不感兴趣的情况(比如静态类)或者我不想在一开始就使用它们。
我 感兴趣的是我可能拥有许多实例的实例和长期生活的“经理”对象的单个实例,这些实例会对这些实例上的某些内容作出反应。一个非常简单的例子来说明我的意思:
public class God {
//the list of followers is really big and changes all the time,
//it seems like a waste of time to
//register/unregister events for each and every one...
readonly List<Believer> Believers = new List<Believer>();
God() {
//...so instead let's have a static event and listen to that
Believer.Prayed += this.Believer_Prayed;
}
void Believer_Prayed(Believer believer, string prayer) {
//whatever
}
}
public class Believer {
public static event Action<Believer, string> Prayed;
void Pray() {
if (Prayed != null) {
Prayed(this, "can i have stuff, please");
}
}
}
对我来说,这看起来比一个实例事件更清晰,更简单,我也不必监视信徒集合中的变化。如果Believer类可以“看到”God-type类,我有时会使用NotifyGodOfPrayer()方法(这是几个类似问题中的首选答案),但是Believer类型的类通常是“模型” - 我不能或不想直接访问上帝班的组件。
这种方法有什么实际缺点吗?
编辑:感谢所有已经花时间回答的人。 我的例子可能不好,所以我想澄清一下我的问题:
如果我在情况中使用这种静态事件,那么
除非这个问题的答案是“是”,否则我并不是真的在寻找替代实施,尽管我非常感谢所有想要提供帮助的人。 我不是在寻找最漂亮的解决方案(我必须将这个奖项放在我自己的版本上,只是为了简短易读和维护:)
答案 0 :(得分:13)
了解事件的一个重要事项是,它们会导致挂钩事件的对象在垃圾收集事件所有者之前不会被垃圾收集,或者直到事件处理程序被取消挂钩。
要把它放在你的榜样中,如果你有一个有多神的多神教神殿,你在那里提升和降级诸如
之神new God("Svarog");
new God("Svantevit");
new God("Perun");
当神被附加到Believer.Prayed
时,神会留在你的RAM中。这会导致你的申请泄露神。
我也会对设计决定发表评论,但我知道你所做的例子可能不是你真实场景的最佳副本。
我似乎更合理的是不要创建从God
到Believer
的依赖关系,以及使用事件。好的方法是创建一个站在信徒和神之间的事件聚合器。例如:
public interface IPrayerAggregator
{
void Pray(Believer believer, string prayer);
void RegisterGod(God god);
}
// god does
prayerAggregator.RegisterGod(this);
// believer does
prayerAggregator.Pray(this, "For the victory!");
在调用Pray
方法时,事件聚合器依次调用God
类的适当方法。要管理引用并避免内存泄漏,您可以创建UnregisterGod
方法或在weak references的集合中保存神,例如
public class Priest : IPrayerAggregator
{
private List<WeakReference> _gods;
public void Pray(Believer believer, string prayer)
{
foreach (WeakReference godRef in _gods) {
God god = godRef.Target as God;
if (god != null)
god.SomeonePrayed(believer, prayer);
else
_gods.Remove(godRef);
}
}
public void RegisterGod(God god)
{
_gods.Add(new WeakReference(god, false));
}
}
快速提示:临时存储事件委托,因为侦听器可能会取消其事件处理程序
void Pray() {
var handler = Prayed;
if (handler != null) {
handler(this, "can i have stuff, please");
}
}
修改强>
考虑到您添加的有关您的场景的详细信息(大量的事件调用者,常量和单个事件观察者)我认为您选择了正确的场景,纯粹出于效率原因。它创建了最少的内存和CPU开销。我不会采用这种方法,但对于你所描述的静态事件,我可能会采用非常实用的解决方案。
我看到的一个缺点是控制流程。如果你所说的事件监听器是在单个实例中创建的,那么我将直接利用单个(反)模式并从God
调用Believer
的方法。
God.Instance.Pray(this, "For the victory!");
//or
godInstance.Pray(this, "For the victory!");
为什么呢?因为那时你可以更精细地控制执行祈祷的动作。如果你决定将你需要将Believer
子类化为一种在某些日子里没有祈祷的特殊类型,那么你就可以控制它。
答案 1 :(得分:0)
我实际上认为having an instance even
会更清晰,更具可读性
查看它更简单,因为一个实例正在捕食,所以他的祈祷事件会被触发。而且我没有看到蚂蚁的缺点。我认为monitor changes
并不比监视静态事件更具喧嚣。但这是正确的方法...
监控列表:
将列表更改为ObservableCollection(并查看NotifyCollectionChangedEventArgs )
通过以下方式监控:
public class God {
readonly ObservableCollection<Believer> Believers = new ObservableCollection<Believer>();
public God() {
Believers = new ObservableCollection<T>();
Believers.CollectionChanged += BelieversListChanged;
}
private void BelieversListChanged(object sender, NotifyCollectionChangedEventArgs args) {
if ((e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Replace) && e.OldItems != null)
{
foreach (var oldItem in e.OldItems)
{
var bel= (Believer)e.oldItem;
bel.Prayed -= Believer_Prayed;
}
}
if((e.Action==NotifyCollectionChangedAction.Add || e.Action==NotifyCollectionChangedAction.Replace) && e.NewItems!=null)
{
foreach(var newItem in e.NewItems)
{
foreach (var oldItem in e.OldItems)
{
var bel= (Believer)e.newItem;
bel.Prayed += Believer_Prayed;
}
}
}
}
void Believer_Prayed(Believer believer, string prayer) {
//whatever
}
}