为什么事件可以';t在派生类中的使用方式与在C#中的基类中的使用方式相同?

为什么事件可以';t在派生类中的使用方式与在C#中的基类中的使用方式相同?,c#,events,C#,Events,在下面的代码中,我希望通过派生/子类化类来扩展类的行为,并利用基类的事件: public class A { public event EventHandler SomeEvent; public void someMethod() { if(SomeEvent != null) SomeEvent(this, someArgs); } } public class B : A { public void someOtherMethod

在下面的代码中,我希望通过派生/子类化类来扩展类的行为,并利用基类的事件:

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')
    }
}
公共A类
{
公共事件事件处理程序SomeEvent;
公共方法()
{
如果(SomeEvent!=null)SomeEvent(this,someArgs);
}
}
B级:A级
{
public void someOtherMethod()
{

如果(SomeEvent!=null)SomeEvent(this,someArgs);//这里的标准做法是在基类上的SomeEvent上有一个受保护的虚拟方法,然后在派生类中调用该方法。此外,出于线程化的原因,在检查null并调用它之前,您希望保留对处理程序的引用

有关为什么读取答案的说明,或描述编译器如何自动创建专用字段的

这里有一个可能的解决办法

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();
    }
}

编辑:根据他人更新的代码。

使用受保护的虚拟打开…方法将其包装:

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

    protected virtual void OnSomeEvent()
    {
        if(SomeEvent!= null)
            SomeEvent(this, new MyArgs(...) );
    }
}
您将在整个.Net中设置此模式-所有表单和web控件都遵循此模式


不要使用前缀Raise…-这与MS的标准不一致,可能会在其他地方引起混淆。

其他人解释了如何回避这个问题,但没有解释为什么会出现这个问题


当您声明类似于事件的公共字段时,编译器将在同一类(或嵌套类)内创建公共事件和私有字段您可以直接访问该字段,例如调用所有处理程序。从其他类中,您只能看到事件,它只允许订阅和取消订阅。

Todd的回答是正确的。通常,您会在整个.NET framework中看到它作为
OnXXX(EventArgs)
方法实现:

public class Foo
{
    public event EventHandler Click;

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

<> P>我强烈建议您在发现您自己编写所有的< <代码> > <代码> >代码> CustomEventHandler < /C>以引发事件之前。请注意:原始代码不起作用的原因是因为您需要访问事件的委托才能提高它,而Cype保持这个委托<代码>私有< /代码>。


C#中的事件由一对方法公开表示,
add_SomeEvent
remove_SomeEvent
,这就是为什么您可以从类外订阅事件,但不能引发它的原因。

我的答案是您不应该这样做

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

如果允许派生类型在它们的祖先中引发事件,那么世界会有多大的不同。注意:这是有效的C代码..(尚未..)


不要使用前缀
Raise…
,它应该始终是
On…
。基类的方法应该是虚拟的。我认为这不是一个好的答案,因为它不能解决您在someOtherMethod和someMethod中委托的外部函数接收与someMet相同的参数的情况hod或其他方法可能有。对于ASP的ObjectDataSource,如果您指定OnSelecting选项,它将传递OnSelecting某些对象、Object sender和ObjectDataSourcesSelectingEventArgs e。现在,如果您以前使用传递给其他函数的参数填充ODSSEA,则此示例将无效。不能吗考虑一个解决方法…委托调用可以简化:SomeEvent?.Invoke(this,someArgs);非常有趣。您是否有相关文档的链接?不是文档本身,而是包含更多关于事件和委托的一般信息。
public class Foo
{
    public event EventHandler Click;

    protected virtual void OnClick(EventArgs e)
    {
        var click = Click;
        if (click != null)
            click(this, e);
    }
}
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
}