什么是C#实现链式事件的最好办法链式、事件、办法

2023-09-04 02:13:03 作者:永远太远

我有以下情形。客户端code只能访问FooHandler,并不直接向美孚实例。

I have the following scenario. The client code only has access to FooHandler, not directly to Foo instances.

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

}

然后客户端将使用FooHandler类的OneFooLoaded事件得到装载FOOS的通知。难道这事件链接'的做正确的事?有什么办法?我不喜欢这种(感觉错了,我不能precisely EX preSS原因),但是如果我想的处理程序是访问的时候,我似乎没有有很多的选择。

Then the clients would use the OneFooLoaded event of the FooHandler class to get notifications of loading of foos. Is this 'event chaining' the right thing to do? Are there any alternatives? I don't like this (it feels wrong, I cannot precisely express why), but if I want the handler to be the point of access I don't seem to have many alternatives.

推荐答案

如果感到不对劲,因为事件更复杂,更外向除了必要的内部通信(我相信这是至少部分如此考虑的事件可以调用多个客户而你知道你只需要通知的,对吧?),那么我提出以下方案。而不是使用事件进行通信的完成富来FooHandler,因为foo是内部反正,你可以添加一个回调参数来富的构造函数或Load方法,其中富可以调用时加载完成。此参数可以只是一个功能,如果你只有一个回调,也可以是一个接口,如果你有很多。以下是我认为你的code看起来与简化内部接口:

If it feels wrong because events are more sophisticated and outward-facing than necessary for your internal communications (which I believe is at least partially true considering events can call multiple clients whereas you know you only need to notify one, right?), then I propose the following alternative. Instead of using events to communicate the completion of Foo to FooHandler, since Foo is internal anyway, you could add a callback parameter to the constructor or Load method of Foo, which Foo can call when it is done loading. This parameter can be just a function if you only have one callback, or it can be an interface if you have many. Here's how I think your code would look with the simplified internal interface:

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

}

请注意,这也可以让你更强类型的FooLoaded委托。

Notice that this also allows you to be more strongly typed with the FooLoaded delegate.

如果,在另一方面,它觉得这是错的,因为事件不应该去通过FooHandler才能到客户端,那么1)我会否认,因为如果客户不希望处理与个人富对象,它不应该被沉从他们的事件在该级别或者,和2)如果你真的想这样做,你可以实现在富一些公共的回调接口,即使foo是私有的,或者使用类似的机制帕维尔建议。然而,我认为,是客户端,如实施减少事件处理程序和区分一个处理器中源,而不必连接(和潜在的断开)的事件来自几十个更小的物体的简单。

If, on the other hand, it feels wrong because the event shouldn't have to go through FooHandler to get to the client, then 1) I would dispute that because if the client doesn't want to deal with the individual Foo objects, it shouldn't be sinking events from them at that level either, and 2) If you really wanted to do that, you could implement some public callback interface on Foo even though Foo is private, or use a mechanism like Pavel suggested. I think, however, that clients like the simplicity of implementing fewer event handlers and distinguishing the source within the one handler rather than having to connect (and potentially disconnect) events from dozens of smaller objects.