什么是在C#中实现链式事件的最佳方式

Vin*_*vic 6 .net c# events

我有以下场景.客户端代码只能访问FooHandler,而不能直接访问Foo实例.

public delegate void FooLoaded(object sender, EventArgs e);

class Foo {
    public event FooLoaded loaded;
    /* ... some code ... */
    public void Load() { load_asynchronously(); }
    public void callMeWhenLoadingIsDone() { loaded(this,EventArgs.Empty); }
}

class FooHandler {
    public event FooLoaded OneFooLoaded;

    /* ... some code ... */

    public void LoadAllFoos() { 
        foreach (Foo f in FooList) { 
            f.loaded += new FooLoaded(foo_loaded);
            f.Load(); 
        } 
    }

    void foo_loaded(object sender, EventArgs e) {
        OneFooLoaded(this, e);
    }

}
Run Code Online (Sandbox Code Playgroud)

然后客户端将使用FooHandler类的OneFooLoaded事件来获取加载foos的通知.这个'事件链接'是正确的吗?还有其他选择吗?我不喜欢这个(感觉不对,我无法准确地表达原因),但如果我希望处理程序成为访问点,我似乎没有很多选择.

Blu*_*kMN 4

如果感觉不对,因为事件比内部通信所需的更加复杂和面向外(我认为这至少部分正确,考虑到事件可以呼叫多个客户,而您知道您只需要通知一个客户,对吧?),那么我提出以下替代方案。您可以向 Foo 的构造函数或 Load 方法添加一个回调参数,而不是使用事件将 Foo 的完成情况传达给 FooHandler,因为 Foo 无论如何都是内部的,Foo 可以在加载完成时调用该参数。如果您只有一个回调,则此参数可以只是一个函数;如果您有多个回调,则它可以是一个接口。我认为使用简化的内部接口您的代码将如下所示:

public delegate void FooLoaded(FooHandler sender, EventArgs e);

class Foo
{
  Action<Foo> callback;
  /* ... some code ... */
  public void Load(Action<Foo> callback) { this.callback = callback; load_asynchronously(); }
  public void callMeWhenLoadingIsDone() { callback(this); }
}

class FooHandler
{
  public event FooLoaded OneFooLoaded;

  /* ... some code ... */

  public void LoadAllFoos()
  {
     foreach (Foo f in FooList)
     {
        f.Load(foo_loaded);
     }
  }

  void foo_loaded(Foo foo)
  {
     // Create EventArgs based on values from foo if necessary
     OneFooLoaded(this, null);
  }

}
Run Code Online (Sandbox Code Playgroud)

请注意,这还允许您使用 FooLoaded 委托进行更强类型的处理。

另一方面,如果感觉不对,因为事件不应该通过 FooHandler 到达客户端,那么 1) 我会对此提出异议,因为如果客户端不想处理单独的 Foo 对象,它也不应该在该级别上接收来自它们的事件,并且 2)如果您确实想这样做,您可以在 Foo 上实现一些公共回调接口,即使 Foo 是私有的,或者使用像 Pavel 建议的机制。然而,我认为客户喜欢实现更少的事件处理程序并区分一个处理程序中的源的简单性,而不是必须连接(并可能断开)来自数十个较小对象的事件。