2008-10-31 21 views
27

在下面的代码,我想通过导出/继承它来扩展一个类的行为,并利用基类的事件:为什么事件不能在派生类中以与C#中的基类相同的方式使用?

public class A 
{ 
    public event EventHandler SomeEvent; 

    public void someMethod() 
    { 
     if(SomeEvent != null) SomeEvent(this, someArgs); 
    } 
} 

public class B : A 
{ 
    public void someOtherMethod() 
    { 
     if(SomeEvent != null) SomeEvent(this, someArgs); // << why is this not possible? 
//Error: The event 'SomeEvent' can only appear on the left hand side of += or -= 
//(except when used from within the type 'A') 
    } 
} 

为什么不是这可能吗?

什么是这种情况的通用解决方案?

回答

29

这里的标准做法是有一个受保护的虚拟方法OnSomeEvent在你的基类,然后调用在派生类方法。此外,由于线程原因,您将希望在检查null并调用它之前保留对处理程序的引用。

有关为什么要读取Jon Skeet's答案或C# specification的说明,它说明编译器如何自动创建专用字段。

这是一个可能的解决方法。

public class A 
{ 
    public event EventHandler SomeEvent; 

    public void someMethod() 
    { 
     OnSomeEvent(); 
    } 

    protected void OnSomeEvent() 
    { 
     EventHandler handler = SomeEvent; 
     if(handler != null) 
      handler(this, someArgs); 
    } 
} 

public class B : A 
{ 
    public void someOtherMethod() 
    { 
     OnSomeEvent(); 
    } 
} 

编辑:更新后的代码基于Framework Design Guidelines section 5.4reminders他人。

+0

不要使用前缀`提高...`,它应该永远是`在...`。基类的方法也应该是虚拟的。 – Keith 2008-10-31 14:55:47

+0

也感谢多线程的提示。 – 2008-10-31 14:55:58

1

与受保护的虚拟在......的方法把它包:

public class BaseClass 
{ 
    public event EventHandler<MyArgs> SomeEvent; 

    protected virtual void OnSomeEvent() 
    { 
     if(SomeEvent!= null) 
      SomeEvent(this, new MyArgs(...)); 
    } 
} 

在派生类中

public class DerivedClass : BaseClass 
{ 
    protected override void OnSomeEvent() 
    { 
     //do something 

     base.OnSomeEvent(); 
    } 
} 

您可以设置这种模式各地的.Net然后覆盖这一点 - 所有形式并遵循它的网络控制。

不要使用前缀升起...... - 这是不是与MS的标准相一致,并可以在其他地方造成混乱。

40

其他解释了如何避开这个问题,但不是为什么它的未来。

在声明公共场式的事件,编译器创建一个公共活动和私人领域。在同一班级(或嵌套的班)中,您可以直接在现场,例如,调用所有的处理程序。从其他课程中,您只能看到仅允许订阅和取消订阅的活动。

5

托德的答案是正确的。通常你会看到整个.NET框架此实现为OnXXX(EventArgs)方法:

public class Foo 
{ 
    public event EventHandler Click; 

    protected virtual void OnClick(EventArgs e) 
    { 
     var click = Click; 
     if (click != null) 
      click(this, e); 
    } 
} 

我强烈建议你考虑EventArgs<T>/EventHandler<T> pattern你发现自己制作的CustomEventArgs/CustomEventHandler所有的方式筹集事件之前。

2

原代码不起作用的原因是因为你需要访问该事件的委托,以提高它和C#保持此委托private

C#中的事件通过一对方法add_SomeEventremove_SomeEvent公开表示,这就是为什么您可以从类外部订阅事件但不提高它的原因。

2

我的答案是,你不应该这样做。

C#很好的执行只有声明/发布事件的类型应该触发/提升它。 如果基类受信任派生有能力提升其事件,创建者将公开受保护的方法来执行此操作。如果它们不存在,那么它就是一个很好的暗示,你可能不应该这样做。

我设计的例子是,如果派生类型被允许在他们的祖先中引发事件,世界将会有多么不同。注意:这是不是有效 C#代码。(但..)

public class GoodVigilante 
{ 
    public event EventHandler LaunchMissiles; 

    public void Evaluate() 
    { 
    Action a = DetermineCourseOfAction(); // method that evaluates every possible 
// non-violent solution before resorting to 'Unleashing the fury' 

    if (null != a) 
    { a.Do(); } 
    else 
    { if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); } 
    } 

    virtual protected string WhatsTheTime() 
    { return DateTime.Now.ToString(); } 
    .... 
} 
public class TriggerHappy : GoodVigilante 
{ 
    protected override string WhatsTheTime() 
    { 
    if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); 
    } 

} 

// client code 
GoodVigilante a = new GoodVigilante(); 
a.LaunchMissiles += new EventHandler(FireAway); 
GoodVigilante b = new TriggerHappy();    // rogue/imposter 
b.LaunchMissiles += new EventHandler(FireAway); 

private void FireAway(object sender, EventArgs e) 
{ 
    // nuke 'em 
} 
相关问题