1

I want to implement a fast way to add listener to an event, my implementation:

public class AccountManager 
{

    public delegate void CheckIfLoggedInListener(EventArgs e);
    public event CheckIfLoggedInListener SetCheckIfLoggedInListener;

    public void CheckIfLoggedIn()
    {                
         if(SetCheckIfLoggedInListener!=null)
             SetCheckIfLoggedInListener(new EventArgs("e"));
    }
}

Right now, I have to set listener first, then invoke the method, which can be easily messed up if other developer doesn't pay attention:

//this will not work, because you invoke the event before subscribing
accountManager.CheckIfLoggedIn();
accountManager.SetCheckIfLoggedInListener += (e) => { Debug.Log(e.param); };

I wonder if there is a way to make the order not mandatory?

Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
weijia_yu
  • 965
  • 4
  • 14
  • 31
  • This is *how* events work. The idea is to only fire the event *if* there's something to fire. – Der Kommissar Jun 16 '17 at 01:21
  • If event listener is not attached then how do you expect the code to work? Do you want the code to generate the listener if not already attached? If the programming has to be done as per the developer's will then exception handing is not at all needed. You shouldn't write bad code just coz developer doesn't want to pay attention. – Chetan Jun 16 '17 at 01:25
  • If you're worried about others not using events properly, don't. If they are any good they'll subscribe to your events properly. Besides, the behavior that you want can be harmful. What if adding a listener to an event after it is called is *intentional*? What if the listener shouldn't be called for, say, the first time the method is called, but should be called for every subsequent method call? As a library developer, trying to "work around" *such* kinds of potential mistakes is a waste of time, and can even harm those who actually know what they are doing. – EvilTak Jun 16 '17 at 01:25
  • by the way, your code may not work properly in any case. Assign to a local first, then check for null. var evt = SetCheckIfLoggedInListener; if(evt!=null) evt(new EventArgs("e")); – cineam mispelt Jun 16 '17 at 03:48
  • @cineammispelt Alternatively, in newer version of .NET, you can use `SetCheckIfLoggedInListener?.Invoke(new EventArgs("e"));` which is a little nicer. VS2015 (and maybe older versions) will actually suggest re-factoring the older event dispatch pattern into this alternate versions. – ozeanix Jun 16 '17 at 05:05
  • @ozeanix according to this, https://stackoverflow.com/questions/5928077/event-invokeargs-vs-eventargs-which-is-faster, this seems they are only different compile version? – weijia_yu Jun 16 '17 at 17:09
  • @ywj7931 The difference is the speculative call of `Invoke` only if the event is not `null`, which happens when an event has no subscribers. Instead of having to pull the event value to a local variable, then do an if test, then invoke the event, you can simply use `?.Invoke()` to do it in one line. You can't do `event(new EventArgs("e"))` because event could be null and you'd throw an exception. – ozeanix Jun 17 '17 at 03:36

1 Answers1

0

You can take a parameter of generic delegate that will be object of type Action<T> and in your case it would beAction<EventArgs> in your method to make sure that:

EDIT:

you would need to tweak it to work, instead of Action<EventArgs>, we would have to use Action<Object,EventArgs>, this post explains why is that

public void CheckIfLoggedIn(Action<object,EventArgs> action)
{
    SetCheckIfLoggedInListener = action.Invoke;

     if(SetCheckIfLoggedInListener!=null)
         SetCheckIfLoggedInListener(null,new EventArgs());
}

and when calling you would need to do like:

//this will not work, because you invoke the event before subscribing
accountManager.CheckIfLoggedIn((o,e) => { Console.WriteLine("event fired");  });

Working DEMO Fiddle here

This way you can make it mandatory for user to register the implementation to be called when event is fired.

Hope it helps!

Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
  • This looks like exactly what am I looking for! Since I want to combine those two lines into one line. I will try this! – weijia_yu Jun 16 '17 at 01:34
  • So I can even add this.SetCheckIfLoggedInListener -= action at the end of the function to prevent memory leak, right? – weijia_yu Jun 16 '17 at 01:35
  • I got the error, "cannot implicaitly convert type 'System.Action to 'CheckIfLoggedInListener' ", any chance you know why? – weijia_yu Jun 16 '17 at 01:59
  • I have update the post with the compilable code and with reference why we need to do like this. – Ehsan Sajjad Jun 16 '17 at 02:30