C# 在任何人订阅之前发起活动

C# 在任何人订阅之前发起活动,c#,C#,我想发起一个活动并通知现有订阅者(如果存在)。但我也希望新的订阅者在订阅后会尽快收到所有事件的通知。这是可能的开箱即用,还是我需要自己实现该功能?现在,我的代码如下所示: public delegate void GridCompiled(int gridsize); public event GridCompiled OnGridCompiled; ctor(){ if (OnGridCompiled != null) OnGridCompiled(gridsize)

我想发起一个活动并通知现有订阅者(如果存在)。但我也希望新的订阅者在订阅后会尽快收到所有事件的通知。这是可能的开箱即用,还是我需要自己实现该功能?现在,我的代码如下所示:

public delegate void GridCompiled(int gridsize);
public event GridCompiled OnGridCompiled;

ctor(){
    if (OnGridCompiled != null)
        OnGridCompiled(gridsize);
}
若事件有0个订阅服务器,则不会引发该事件,并且在引发事件后订阅的订阅服务器也不会引发该事件


如果我需要自己实现,我的选项是什么?

没有对引发事件的跟踪,因此您必须自己实现该功能。您需要一个列表来按顺序存储以前的事件参数,并在添加新事件侦听器时执行相关事件:

class Example {
    private readonly List<GridCompiledEventArgs> m_previousEventArgs = new List<EventArgs>();
    private EventHandler<GridCompiledEventArgs> m_gridCompiled;

    public event EventHandler<GridCompiledEventArgs> GridCompiled {
        add {
            //Raise previous events for the caller
            foreach(GridCompiledEventArgs e in m_previousEventArgs) {
                value(this, e);
            }
            //Add the event handler
            m_gridCompiled += value;
        }
        remove {
            m_gridCompiled -= value;
        }
    }

    protected virtual void OnGridCompiled(GridCompiledEventArgs e) {
        if (m_gridCompiled != null) {
            m_gridCompiled(this, e);
        }
        m_previousEventArgs.Add(e);
    }
}
类示例{
私有只读列表m_previousEventArgs=新列表();
私有事件处理程序m_gridCompiled;
已编译公共事件事件处理程序{
加{
//为调用方引发以前的事件
foreach(m_previousEventArgs中的GridCompiledEventArgs e){
价值(本,e);
}
//添加事件处理程序
m_+=值;
}
除去{
m_-=值;
}
}
受保护的虚拟void OnGridCompiled(GridCompiledEventArgs e){
if(m_gridCompiled!=null){
m_(本,e);;
}
m_previousEventArgs.Add(e);
}
}
对于这个解决方案你有两件事要考虑。如果您想解决这些问题,您的解决方案将变得更加复杂:

  • 如果事件处理程序可以更改
    GridCompiledEventArgs
    (例如,向调用者返回状态),则事件参数将与这些更改一起存储在上一个事件列表中。此外,如果事件处理程序保留对事件参数的引用,那么它们甚至可能在以后更改它。如果您不想这样做,则必须在引发“历史”事件之前,在
    m_previousEventArgs
    中存储一个副本,并创建另一个副本
  • 最佳做法是允许派生类重写
    OnGridCompiled
    方法,而不是处理事件或更改其行为。如果派生类更改
    OnGridCompiled
    以截获事件,并且在某些情况下不引发该事件,则此行为并不总是适用于“历史”事件,因为它是在没有
    OnGridCompiled
    的情况下引发的(这可能正是您想要的行为)。如果您想改变这一点,您必须实现一个经过
    OnGridCompiled
    的解决方案。如果这是您的一个选项,您可以通过将类
    密封
    OnGridCompiled
    方法
    私有
    而不是
    受保护的虚拟
    来避免此问题

  • 可能有点过头了,但Rx.Net提供了一种
    重播方法:在我看来,您似乎在寻找一个消息队列,而不是事件。谢谢。点2不适用于我的情况,因为类已经被密封,但是我需要考虑点1。不过我还有一个问题。为什么不编写
    私有事件处理程序m\u gridCompiled?因为这不是一个事件
    GridCompiled
    是事件
    m_gridCompiled
    只是存储事件处理程序的后台字段。整个构造称为事件方法syntaxt。也许我应该扩展一下我的问题。我将event理解为在引发事件时调用的方法列表,我认为关键字
    event
    对于这种行为至关重要。但在上面的示例中,您可以将事件处理程序添加到
    m_gridCompiled
    (这不是事件)并像引发事件一样引发它。关键字
    event
    是否只是为了代码清晰?事件处理程序本质上是一个委托。委托用于许多不同的目的(事件、回调等)。可以使用委托定义事件,但这会导致不同的语义。事件具有添加/删除语义。这意味着您只能添加或删除处理程序;其他操作(如分配)是不可能的。如果不使用
    event
    ,则具有字段/属性语义,这是不同的。