开发者

Is there a way to accomplish the equivalent of passing an "event" by reference?

I put "event" in quotes because I realize that it's a bit of syntax sugar, rather than a true type.

I have some events which are simply chained to matching events in another class. So when the event is raised, the passage is like

Raiser -> Proxy -> Subscriber

So in the Proxy class I have a common pattern like this:

Raiser.SomeEvent += 
    (_, args) =>
    {
        if (this.SomeEvent != null)
            this.SomeEvent(this, args);
    };

To tidy up my code I wanted to move this out to another method that returns a new delegate that wraps the above event-calling code:

public static EventHandler GetHandlerDelegate(EventHandler handler, Object sender)
{
    return
        (_, args) =>
        {                
            if (handler != null)
                handler(sender, args);
        };
    }      

And th开发者_Go百科en in Proxy I can just do:

Raiser.SomeEvent += GetHandlerDelegate(this.SomeEvent, this);

Which is much neater.

Well this is fine as long as Subscriber doesn't decide to subscribe to Proxy.SomeEvent after the above call. Unfortunately I'm not passing the "event" around by reference as I'd hoped; I now understand that I'm just passing the invocation list, so when OtherClass.SomeEvent happens and that anonymous method is called and invokes the "event" (delegate) it was given, only the delegates that had been added to that event at the time I called GetHandlerDelegate() will be called. While that would actually suffice for my current situation, it's really not acceptable to code it that way.

I've read some other SO questions and I gather there is something called Reactive Extensions that might help, but at this time I'm looking for a simpler solution if there is one. (If not, I just won't do this.)

Is there another way I can accomplish what I'm trying to do, without said drawback?


If this question is unclear, please see my answer which hopefully helps clarify it.


EDIT: Okay, I think I get the point now. It's actually quite simple. You should be able to write the proxy to just have an event, and then make the proxy itself subscribe to the Raiser's event, like this (just for EventHandler - I'll come to that later on):

Proxy proxy = new Proxy();
raiser.SomeEvent += Proxy.Handler;

// Then in the subscriber...
proxy.ProxiedEvent += (whatever)

// And the proxy class...
public class Proxy
{
    public event EventHandler ProxiedEvent;

    public void Handler(object sender, EventArgs e)
    {
        EventHandler proxied = ProxiedEvent;
        if (proxied != null)
        {
            // Or pass on the original sender if you want to
            proxied(this, e);
        }
    }
}

Now, the difficulty here is getting it to work generically. I can't currently think of any way of doing that, although I'm somewhat distracted right now.

Is this the sort of thing you were thinking of, or does it at least help you think about things differently?


Since my original goal of doing:

Raiser.SomeEvent += GetHandlerDelegate(this.SomeEvent, this); 

is impossible, I've compromised and come up with this:

Raiser.SomeEvent += (_, args) => RaiseEvent(this.SomeEvent, this, args); 

Whereas GetHandlerDelegate() would return a delegate which raises the event, RaiseEvent() simply (you guessed it) raises the event.

public static void RaiseEvent(EventHandler _event, Object sender, EventArgs args)
{
    if (_event != null)
        _event(sender, args);
}

And to support events using custom EventArgs:

public static void RaiseEvent<TArgs>(EventHandler<TArgs> _event, Object sender, TArgs args)
    where TArgs : EventArgs
{
    if (_event != null)
        _event(sender, args);
}

I've put these methods in a static helper class, so the actual call is slightly uglier; here's an example:

ViewControl.OpenFilesetClick += (_, args) => EventHelper.Raise(OpenFilesetClick, this, args); 

(I also renamed the method to Raise() and dropped the optional this from the event name being passed).

But I'm not entirely convinced if this is worthwhile, considering the alternative was arguably easier to read:

ViewControl.OpenFilesetClick += (_, args) => 
{
    if (OpenFilesetClick != null)
        OpenFilesetClick(this, args);
};

Anyway, it was an interesting way to learn more about how events and delegates work (or how they don't work).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜