1

I have a large number of instances of a class. Those instances can fire an event. The only important thing for that event is which instance fired it.

I have another instance of a class (and maybe in the future more classes), that want to subscribe to that event.

For example (in pseudo C#):

class Button
{
   public event EventHandler<Button> PressedTheButtonEvent();
}

class EventConsumer
{
   private int buttonsPressed;
   private OnButtonPressed(Button buttonReference)
   {
      buttonReference.color = blue;
      buttonsPressed++;
   }
}

In order for the EventConsumer class to subscribe to the event, it needs to subscribe to every instance's EventHandler<Button> delegate. Given the fact, that I have a large number of buttons, I do not like iterating over every existing button and susbcribing. This is prone to errors, especially if, for some reason, a button is created, after I have iterated over them.

I have come up with 2 solutions:

  1. Make the PressedTheButtonEvent static.
  2. Create another class, that has a single event. Instead of every button firing its event, call a method on the new class to do so.

I don't like 2. because the new class is tightly coupled with the Button class. I also don't like much 1., because if for some reason, someone forgets to unsubscribe from the static event, those instances will not be garbage collected.

Are there any other solutions?

I'm writing C#, but I tried to keep the question in a more general context.

  • You might find Routed Events in WPF relevant. You can read more about them here. – Robert Harvey Oct 14 '16 at 23:19
1

2 is the mediator pattern.

You can get around the tight coupling by:

  • Injecting a IMediator interface with just 'publish' and 'listen'

  • By using a factory method to create the buttons and wire up the event at the same time

  • Have a ButtonViewModel which takes the injected mediator, the event and wires up to the ButtonView which contains the button.

If you are doing C# with wpf the viewmodel is the way to go

1

Use a lambda for the event handler. The lambda captures all information necessary for it work at the point of its creation.

It's like the classic calculator example, where you add numeric buttons in a loop:

for (n in 0 .. 9)
{
    let button = createNumberButton(n);
    button.Click += (source, event) => { numberButtonPressed(n); };
}

No need to iterate over a list of buttons for event handling, no need to set up an identity hash map for the instances, &c. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.