2010-08-17 40 views
9

我正在使用C#3.0。遵循这一标准的事件模式,我有:我的班级是否应该订阅自己的公共活动?

public event EventHandler<EventArgs> SomeEventHappens; 

    protected virtual void OnSomeEventHappens(EventArgs e) 
    { 
     if (SomeEventHappens != null) 
     { 
      SomeEventHappens(this, e); 
     } 
    } 

    private object _someProperty; 

    public object SomeProperty 
    { 
     get 
     { 
      return _someProperty; 
     } 
     private set 
     { 
      if (_someProperty == value) 
      { 
       return; 
      } 
      OnSomeEventHappens(EventArgs.Empty); 
      _someProperty = value; 
     } 
    } 

在我的同班我想的时候采取一些行动SomeProperty变化。我看到它的方式,我有3个替代品:

1)做我的SomeProperty二传手。有些事情是因为我试图订阅每件事物的哲学应该做一件事而做得很好,所以我错了。把东西塞进一个二传手似乎是违背了这一点,或者至少有倾向于。

2)在OnSomeEventHappens做东西。再次,似乎有点反对保持这个简单的作品。此外,如果此方法被覆盖,如果实现者不调用基方法,可能会失去功能。

3)是否订购了SomeEventHappens。对我来说,就封装而言,这似乎是一个合适的选择,而且看起来很干净。再次,如果OnSomeEventHappens被覆盖,可能会产生影响。

也许有更优雅的东西?我无法在方案2和方案3之间做出决定,而且我很好奇最佳实践是什么。毕竟,最安全的地方也许就是财产创造者。

想法?

更新: 感谢您的好评和下面的答案。我已经了解到,让班级订阅自己的活动是“可以的”,尽管在我的情况下,我倾向于因为开销而不愿意这样做。我已经考虑到了我的虚拟方法的潜在覆盖者的行为,以及我想要发生的事情。

在我的现实世界中,我并不想在未设置属性的情况下引发事件。由于下面的答案指导了我的思考过程,所以我认为我可以选择1,因为开销较低,从继承者那里行为不当的风险降低,并且通常对我来说更合适。再次感谢!

回答

2

如果您从某些常见位置(属性过程或其他函数)调用SomeEventHappens和OnSomeEventHappens,那么您不必担心忽略引发事件的重写器。我宁愿重写一个函数,而不是处理事件,因为它的开销较小。

+0

我选择了这个答案,因为它是我最终做的最接近的解释。我从其他答案中学到了很多东西。谢谢! – 2010-08-18 12:01:29

1

你会总是想采取这个动作,或者你想要订阅和取消订阅?在后一种情况下,选项3显然是一个好主意。

您希望采取的行动种类另一个班级可能希望采取的行动?再次,这将倾向于选项3.

您希望采取的行动固有地设置属性的一部分?如果是这样,那么行动1可能是明智的。

选项3听起来像一个很好的“轻触”方法给我。

+0

在我的真实世界的情况(而不是简单的版本以上),我不想订阅和取消订阅。该属性实际上是一个枚举,并根据所设置的枚举值引发大量不同事件中的任何一个。由于我正在筹备赛中提升赛事,因此我可以在那里获得所有赛事信息。从头脑的角度来看,不要订阅所有这些事件,只需要在二传手中处理它就可能更轻。 – 2010-08-17 19:30:54

2

在.NET以外的对象框架中,订阅自己事件的对象主要是因为这样的事情导致循环引用可能使对象无限期地活动而不被接受。这不是一个问题。NET,但对于我来说,对于一个物体以这种方式摸索它仍然是“奇怪的”。

如果一个类总是需要知道属性发生了什么变化,那么最好的办法就是使OnSomeEventHappens方法变为虚拟,并在需要额外信息的后代类中覆盖它。在事件触发方法中放置代码是可以的。事件触发的方法正是在这种情况下,每个想要触发该事件的人都有统一的方法来执行此事。

如果您只是偶尔需要了解属性更改的时间,那么我认为订阅和取消订阅该事件是适当的。

+0

我最初的想法是,它可能会被一个班级自订。从其他一些答案和你的答案来看,它似乎不是.NET中的问题。如果我在我的OnSomeEventHappens虚拟方法中添加了一些额外的逻辑,我需要采取信念的飞跃,即覆盖者将在其覆盖中调用基础版本。对于我的目的,我仍在辩论这是否会成为问题。 – 2010-08-17 19:12:34

+1

虚拟和事件之间的主要区别在于事件应该是/应该是无约束的,对于事件处理程序应该做什么有很少或没有要求,而虚拟本质上是契约式的。应该记录虚拟方法来描述何时以及是否应该在扩展或覆盖行为时被后代称为虚拟方法。 IOW,如果你的覆盖者是优秀的程序员,这不应该成为一个主要问题。如果你的覆盖者是普通公众/随机白痴,那么可能需要防御工事。 – dthorpe 2010-08-17 19:22:25

+0

任何覆盖都有可能成为我未来的自我的99%的机会,无论是我把它带入优秀程序员的类别还是随机白痴的争论! :) – 2010-08-17 19:34:50

1

如果您拥有自己的对象的状态,捕捉事件对我来说听起来是错误的。我会用一个单独的虚拟方法去。不要干涉你的活动,并希望孩子们扔它。也许这看起来像这样:

private object _someProperty; 
    public object SomeProperty 
    { 
     get 
     { 
      return _someProperty; 
     } 
     private set 
     { 
      if (_someProperty != value) 
      { 
       OnSettingSomeProperty(_someProperty, value); 
       OnSomeEventHappens(EventArgs.Empty); 
       _someProperty = value; 
      } 
     } 
    } 

    protected virtual void OnSettingSomeProperty(object oldValue, object newValue) 
    { 
     // children can play here, validate and throw, etc. 
    } 
+0

这可以追溯到选项1.虽然我不认为我会使OnSettingSomeProperty虚拟,可能是私人的,所以我可以保证它不会被错过。我并不关心事件的发生,而是首先提出的事件。 – 2010-08-17 19:57:47

+0

@jomtois,OnSettingSomeProperty的意图是允许后代注入行为。很明显,你有什么,但不是你原来的帖子中的内容。也许你对任何人的回答都不满意,因为你没有在你的问题中定义“做什么......”。 – Marc 2010-08-17 21:06:21