2

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 then 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.

Community
  • 1
  • 1
Igby Largeman
  • 16,495
  • 3
  • 60
  • 86

2 Answers2

3

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?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Jon, thank you for the answer, but as a mere sub-Skeet mortal I'm having trouble seeing the implied details. In other words, I'm lost. Could you pelase elaborate? How can this be applied to my problem? – Igby Largeman Aug 17 '11 at 15:46
  • @Charles: It's possible I've misunderstood the question... I'm finding it slightly hard to follow at the moment, but that's as likely to be my fault as yourself. However, the EventWrapper here is basically a way of abstracting the "something you can subscribe to and unsubscribe from" concept. That's what an event is, fundamentally. You're not going to be able to get the event itself as a delegate, because it's *not* a delegate. – Jon Skeet Aug 17 '11 at 16:09
  • I seem to have a history of poorly expressing my questions, so I'm sure I'm to blame for any confusion, not you! Essentially I wanted to pass any delegate _dA_ to a method and get back a new delegate _dB_ that invokes _dA_, with the kicker being that if _dA_'s invocation list then changes, I want the _updated_ invocation list to be invoked when I call _dB_. (I tried this without using an event at all - just a delegate - to confirm for myself that the problem is really about how _delegates_ work). – Igby Largeman Aug 17 '11 at 18:00
  • @Charles: That in itself is impossible, because a delegate's invocation list can never change - delegates are immutable. However, I think I now see what you mean, and will edit my answer... – Jon Skeet Aug 17 '11 at 18:30
  • Thans again for your efforts. It seems immutability doesn't have to be a barrier though: suppose a delegate was just like, say, a string, and I could pass it around with the ref keyword - then my problem would be solved. Regarding your edit: my goal was to have the proxy subscribe to the raiser using the least code - e.g. a simple lambda. I understand what you've shown me, but that's the long version. I have found a compromise though; I'll post an answer. – Igby Largeman Aug 17 '11 at 19:16
  • @Charles: A delegate *is* like a string. An event isn't - they're very different things. – Jon Skeet Aug 17 '11 at 19:40
  • You are absolutely right of course, my comment came from an incorrect premise - my brain has been out to lunch. What I really want is the ability to pass _delegateA_ by reference to `GetHandlerDelegate()` so that it gives back a _delegateB_ that has a reference to _delegateA_, rather than a copy of it. Unfortunately ref parameters cannot be used in anonymous methods. (And I would like to apply the same wish to events, their difference from delegates notwithstanding.) – Igby Largeman Aug 17 '11 at 20:15
0

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).

Igby Largeman
  • 16,495
  • 3
  • 60
  • 86