Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Event interrupt when exception is thrown

Discussion in 'Scripting' started by Eyellen, Sep 7, 2023.

  1. Eyellen

    Eyellen

    Joined:
    Aug 9, 2021
    Posts:
    27
    Sorry if this has been answered but I couldn't find post about it. I'm having a problem when exception is thrown in on of methods assigned to event. I'm actively using observer pattern and I have a GameManager that fires event if game state is changed. I noticed the execution doesn't perform correctly when there had been thrown exception in one of assigned methods. So I want to know is there a way to prevent event from interrupting it's methods execution even if exceptions are thrown, I know I should fix exceptions but still is it possible to keep event working ?
     
  2. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    To be fair, if code gets a compiler error, you can't judge anything until that error is fixed.. words of advice!
     
  3. Eyellen

    Eyellen

    Joined:
    Aug 9, 2021
    Posts:
    27
    Is it compilation error? If it was I wouldn't be able to even run the game. It's the runtime error, isn't it?
     
    Bunny83 likes this.
  4. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Negative, once one set of the code stops thinking because of an error, it won't run anything past that. Especially if that determinate point sets the bar for something else. So like I said, always fix the first error in line, all else should be easy after that point. :)

    I'm always able to keep running different parts of my projects when a small section fails, as far as I know, the code tries to always protect itself, but it does the best it can with user error(run-away for loops and what-not)..

    Just imagine it's all a road map to the end goal, and errors are road blocks, how many road blocks do you think are acceptable? I say none, and I feel the compiler feels the same..

    Now however, you could have two un-related issues, and are thinking they're the same.. But how would you ever know, until you rid yourself of all other errors?
     
    Eyellen likes this.
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,842
    Methods generally exit execution if an error is thrown, either unintentionally or intentionally (as in when you throw
    new System.Exception();
    , for example).

    What exception are you getting? Can you guard your logic against it?
     
    Eyellen likes this.
  6. Eyellen

    Eyellen

    Joined:
    Aug 9, 2021
    Posts:
    27
    It was just a NullRef exception, it was just interesting to know if there is a way to protect event from this because it felt strange that all execution fails if one method throws exception but after this explanation
    it feels ok
     
  7. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,842
    Well a null ref is one of the most common error you will encounter, and your code should be built to handle null values, or should not be encountering nulls in the first place: https://forum.unity.com/threads/how-to-fix-a-nullreferenceexception-error.1230297/

    It can be as simple as just checking
    if (something == null)
    and logging a warning, then executing the logic early with
    return;
    .

    Depends where the null-ref is happening of course. Your first course of action should of couse be to investigate the cause and prevent it from happening.
     
    Eyellen likes this.
  8. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,013
    To answer the question, exception handling is done with try catch:

    try {}
    catch(Exception ex) {}
     
    Eyellen, faUnity and lordofduct like this.
  9. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    284
    My personal solution to this issue was to create my own event implementation. Then you can invoke the event for each observer in a loop with a Try / Catch, capture the single exception, or compile multiple exceptions into a list if you go beyond one exception, then rethrow the original captured exception, or throw a new AggregateException with the multiple exceptions nested as inner exceptions. That's how my personal implementation of an update manager works, so when using that to update instead of Update(), if 100 objects are updating, and 50 of them randomly throw an exception, the other 50 all still update, and all of the rest of the logic in the frame is still executed. The exception(s) are caught and logged immediately after the event is invoked by the update manager that is using my custom event type for the update.
     
    Eyellen and Bunny83 like this.
  10. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,379
    When you call your event you can wrap the event call in a try/catch:

    Code (csharp):
    1. public event System.Action SomeEvent;
    2.  
    3. void SomeCode()
    4. {
    5.     //doing stuff
    6.  
    7.     //calling event
    8.     try
    9.     {
    10.         SomeEvent?.Invoke();
    11.     }
    12.     catch (System.Exception ex)
    13.     {
    14.         Debug.LogException(ex);
    15.     }
    16.  
    17.     //doing rest of stuff uninterrupted by exception in SomeEvent callbacks
    18. }
    Note though that if one of the callbacks/handlers in SomeEvent throws an exception, not all handlers/callbacks registered will complete. So say 3 handlers are registered, 1st completes, 2nd throws, 3rd never gets reached.

    Of course, you could technically call 'GetInvocationList' on the event delegate to enumerate the individual handlers/callbacks:
    https://learn.microsoft.com/en-us/dotnet/api/system.delegate.getinvocationlist?view=net-7.0

    Something like:
    Code (csharp):
    1. public event System.Action SomeEvent;
    2.  
    3. void SomeCode()
    4. {
    5.     //doing stuff
    6.  
    7.     //calling event
    8.     foreach (var d in SomeEvent.GetInvocationList())
    9.     {
    10.         try
    11.         {
    12.             (d as System.Action).Invoke();
    13.         }
    14.         catch (System.Exception ex)
    15.         {
    16.             Debug.LogException(ex);
    17.         }
    18.     }
    19.  
    20.     //doing rest of stuff uninterrupted by exception in SomeEvent callbacks
    21. }
    And that could even be wrapped into its own extension method for ease of reuses:
    Code (csharp):
    1.     static class DelegateExtensions
    2.     {
    3.  
    4.         public static void InvokeSafely<T>(this T del, params object[] args) where T : System.Delegate
    5.         {
    6.             if (del == null) return;
    7.  
    8.             foreach (var d in del.GetInvocationList())
    9.             {
    10.                 try
    11.                 {
    12.                     (d as T).DynamicInvoke();
    13.                 }
    14.                 catch (System.Exception ex)
    15.                 {
    16.                     Debug.LogException(ex);
    17.                 }
    18.             }
    19.         }
    20.  
    21.     }
    (note the implications of creating collections in the moment and dynamically invoking... this is garbage heavy and slower than just calling the event directly)

    But I mean, at this point... wtf are we even doing? Fix your exception! This isn't how events should work.

    And if for whatever reason you need to allow the exception in your specific event handler. Wrap that with a try/catch in place.

    Code (csharp):
    1. void MyEventHandler()
    2. {
    3.     try
    4.     {
    5.          //do stuff
    6.     }
    7.     catch {} //so my exception doesn't bubble out
    8. }
     
    Eyellen, faUnity and CodeRonnie like this.
  11. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    284
    This is the true answer, of course. Exceptions are unacceptable!

    I didn't create my own event class for the ability to continue invoking observers in the face of individual exceptions, I had other more important reasons that served as the original motivation, it's just something extra that I added while I was in there. It should only be relevant in the situation where I'm encountering an exception, which shouldn't be happening and is going to be fixed right now, today!
     
    Eyellen and Bunny83 like this.