Exception handling in delegate

12,652

Here are four approaches.

In approach "A", the Exception is multi-cast to all subscribers. This is done by including the Exception instance as an "innerException" field in your custom EventArgs class.

In approach "B", the Exception is handled "out-of-band" (not multi-cast, not part of the event mechanism) by calling a separate delegate.

In approach "C", you have an application-level exception handler. You want to inform it that this exception happened as part of processing ReceiveCompleted. Do this by defining (and throwing) a ReceiveCompletedException, which has an "innerException" field to contain the actual exception.

In approach "D" (no code given below) you don't care that the exception happened in ReceiveCompleted code. You just need a generic place to handle exceptions. This is known as "application-level exception handling". See Catch-all error handling on application level?

Approach A:

// ========== A: multi-cast "innerException" integrated into EventArgs ==========

public class ReceiveCompletedEventArgs_A
{
    public string myData;   // Set on successful events (no exception).
    public Exception innerException;    // Set when there is an exception.
}

public delegate void ReceiveCompletedEventHandler_A(object sender, ReceiveCompletedEventArgs_A args);

// The Publisher of ReceiveCompleted event, with "innerException" mechanism.
public class RCPublisher_A
{
    public event ReceiveCompletedEventHandler_A ReceiveCompleted;

    public void OnRaiseReceiveCompletedEvent(string myData)
    {
        ReceiveCompletedEventArgs_A rc;
        try
        {
            rc = new ReceiveCompletedEventArgs_A { myData = myData };
            // Uncomment below line, to see an exception being handled.
            //throw new Exception("Testing exception handling");
        }
        catch (Exception ex)
        {
            rc = new ReceiveCompletedEventArgs_A { innerException = ex };
        }

        if (ReceiveCompleted != null)
            ReceiveCompleted(this, rc);
    }
}

// A Subscriber of ReceiveCompleted event, with "innerException" mechanism.
public class RCSubscriber_A
{
    public void Initialize(RCPublisher_A rcp)
    {
        rcp.ReceiveCompleted += OnReceiveCompleted;
    }

    private void OnReceiveCompleted(object sender, ReceiveCompletedEventArgs_A rc)
    {
        if (rc.innerException != null)
        {
            // Handle the exception
        }
        else
        {
            // Use rc.myData
        }
    }
}

Approach B:

// ========== B: "Out-of-band" handling of exceptions; not multi-cast ==========
// (Successful events are multi-cast, but exceptions are sent to a single delegate.)

public class ReceiveCompletedEventArgs_B
{
    public string myData;   // Set on successful events (no exception).
}

public delegate void ReceiveCompletedEventHandler_B(object sender, ReceiveCompletedEventArgs_B args);

public delegate void ExceptionDelegate(Exception ex);

// The Publisher of ReceiveCompleted event, with out-of-band Exception handling.
public class RCPublisher_B
{
    // Called when the event is successful (no exception).
    public event ReceiveCompletedEventHandler_B ReceiveCompleted;

    // Called when there is an exception.
    public ExceptionDelegate exceptionDeleg;

    public void OnRaiseReceiveCompletedEvent(string myData)
    {
        try
        {
            ReceiveCompletedEventArgs_B rc = new ReceiveCompletedEventArgs_B { myData = myData };
            // Uncomment below line, to see an exception being handled.
            //throw new Exception("Testing exception handling");

            if (ReceiveCompleted != null)
                ReceiveCompleted(this, rc);
        }
        catch (Exception ex)
        {
            if (exceptionDeleg != null)
                exceptionDeleg(ex);
            // What to do if there is no exceptionDeleg:
            //   If below line is commented out, unhandled exceptions are swallowed.
            //   Uncomment, to throw them to your app-level exception handler.
            else throw;
        }

    }
}

// A Subscriber of ReceiveCompleted event, with out-of-band Exception handling.
public class RCSubscriber_B
{
    public void Initialize(RCPublisher_B rcp)
    {
        rcp.ReceiveCompleted += OnReceiveCompleted;
        // CAUTION: Overrides any other exception delegate.
        // If you need multi-casting of the exception, see Approach A.
        rcp.exceptionDeleg = RCExceptionOccurred;
    }

    private void OnReceiveCompleted(object sender, ReceiveCompletedEventArgs_B rc)
    {
        // Use rc.myData
    }

    private void RCExceptionOccurred(Exception ex)
    {
        // Use ex.
    }
}

Approach C:

// ========== C: Wrap "innerException" into custom Exception, for app-level handler ==========
// Similar to B, but instead of adding ExceptionDelegate and exceptionDeleg,
// Define a custom exception type, and throw it.
// Catch it inside your app-level handler.
public class ReceiveCompletedException : Exception 
{
    public Exception innerException;
}

public class RCPublisher_C
{
    public event ReceiveCompletedEventHandler_B ReceiveCompleted;

    public void OnRaiseReceiveCompletedEvent(string myData)
    {
        ReceiveCompletedEventArgs_B rc;
        try
        {
            rc = new ReceiveCompletedEventArgs_B { myData = myData };
            // Uncomment below line, to see an exception being handled.
            //throw new Exception("Testing exception handling");

            if (ReceiveCompleted != null)
                ReceiveCompleted(this, rc);
        }
        catch (Exception ex)
        {
            throw new ReceiveCompletedException{ innerException =  ex };
        }
    }
}

// ...
// In your app-level handler:
        // ...
        catch (ReceiveCompletedException rce)
        {
           // If it gets here, then an exception happened in ReceiveCompleted code.
           // Perhaps you have some graceful way of restarting just that subsystem.
           // Or perhaps you want a more accurate log, that instead of just saying
           // "Exception XYZ happened" (the inner exception), logs that it was
           // ReceiveCompleted that has the problem.
           // use rce.innerException
        }
        // ...
Share:
12,652

Related videos on Youtube

Tim Windsor
Author by

Tim Windsor

Updated on June 04, 2022

Comments

  • Tim Windsor
    Tim Windsor almost 2 years

    I have the following code which gives me an "Exception was unhandled by user code" when it tries to throw an error:

    private static void _msgQ_RecieveCompleted(object sender, ReceiveCompletedEventArgs e)
    {
        try
        {
            //queue that have received a message
            MessageQueue _mq = (MessageQueue)sender;
    
            //get the message off the queue
            Message _mqmsg = _mq.EndReceive(e.AsyncResult);
            throw new Exception("This is a test exception by Tim");
    
            //set the values back into a formatted struct 
            //now process your SQL....
            Azure_SQL _azuresql = new Azure_SQL();
            _azuresql.writeMessageToStorage((_TwitterStreamFeed)_mqmsg.Body);
    
            //refresh queue just in case any changes occurred (optional)
            _mq.Refresh();
    
            //tell MessageQueue to receive next message when it arrives
            _mq.BeginReceive();
    
            return;
        }
        catch 
        {
            throw;
        }
    }
    

    It is called by the following method (previously the snippet):

    public void MSMQ_GetMessage(string _MQ_Path)
            {
                try
                {
    
                    //set the correct message queue
                    MessageQueue _msgQ = new MessageQueue(_MQ_Path, QueueAccessMode.ReceiveAndAdmin);
                    //set the format of the message queue
                    _msgQ.Formatter = new XmlMessageFormatter(new Type[] { typeof(_TwitterStreamFeed) });
                    try
                    {
                        _msgQ.ReceiveCompleted += new ReceiveCompletedEventHandler(_msgQ_RecieveCompleted);
                    }
                    catch
                    {
                        throw;
                    }
    
                    IAsyncResult _result = _msgQ.BeginReceive();
                    _asyncList.Add(_result); // asyncList is a global variable of type System.Collections - > this allows the callback to remain open and therefore nit garbage collected while the async thread runs off on it's own
                }
                catch (Exception _ex)
                {
                    throw new Exception("_msgQ_get Message threw the following error :- " + _ex);
                }
                catch
                {
                    throw;
                }
    
            }
    

    Can you help me understand why the error isn't thrown back to the ReceiveCompletedEventHandler call? I get that it's executing the code on a different thread, but I don't understand from the MSDN examples how to capture the exception. I was expecting the Exception to be return to the call try/catch block.

    • muratgu
      muratgu almost 11 years
      you are re-throwing the exception, not handling it?
    • gustavodidomenico
      gustavodidomenico almost 11 years
      Sorry, but in your code snipper you just subscribing a handler for your event. Your not firind it. There is no meaning of the second try/catch block (unless you have a custom add/remove handler for the event).
    • Tim Windsor
      Tim Windsor almost 11 years
      I have updated the snippet with the full method to hopefully be clear. it has got messy with try / catch blocks as I've just been trying everything I can think of to get the exception back from the delegate.... thanks
    • Dialecticus
      Dialecticus almost 11 years
      You are trying to catch the exception in the wrong place. Exception is not thrown in the line where you register your handler to the event, but in the line where that event is fired (invoked). This line is not shown in the code above.
    • Dialecticus
      Dialecticus almost 11 years
      Also, all these catch { throw; } bits do not contribute to anything, and do not do anything, and should just be removed.
    • Dialecticus
      Dialecticus almost 11 years
      In another question Invoke method is visible, and so the answer to it is correct. You need to put the catch code around your Invoke.
    • Dialecticus
      Dialecticus almost 11 years
      I guess that there is a corresponding EndReceive, and that there is EndInvoke inside it too. In asynchronous calls you catch the exceptions in EndInvoke, not BeginInvoke.
    • Tim Windsor
      Tim Windsor almost 11 years
      yeah there's an EndReceive in the method _msgQ_RecieveCompleted, which is the handler method. I'm not using BeginInvoke or EndInvoke ... I'll investigate on MSDN. thanks for the pointer.. :-)
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    .. and to the person who was suggesting a solution in the catch block, and then deleted their answer after being downvoted: I figured out what you were trying to say. Don't be discouraged. (I wasn't the one who downvoted you.)
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    and see @gustavo's comment; there may also be more you need to do regarding the ReceiveCompleted, however, that is a separate issue. Can you say more about what you hope to have happen?
  • Tim Windsor
    Tim Windsor almost 11 years
    thanks both for responding... what I'm trying to understand is how to throw the exception that occurred within the delegate method _msgQ_RecieveCompleted. For example I receive the message and then call an SQL method (referenced dll) to write the data. if this raises an exception I want to stop the process and print something to a console app say. I think what @Gustavo is saying, is that I need to do 'more' to ReceiveCompletedEventHandler to enable this. I guess that's what I don't understand - sorry new to delegates and MQ -> have been trying to find any examples with no luck..
  • Tim Windsor
    Tim Windsor almost 11 years
    Ridoy - ahh to be clear - the exception is not being caught by the calling block (ReceiveCompletedEventHandler block) it is being caught in the delegate class and when it hits the 'throw' I get the unhandled exception error in my code..
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    @Tim. OK, I was reading this backwards. My answer as written won't help -- it is in the wrong place You can't "throw" the exception where you are trying to. You have to catch it in the catch block, and CALL some method yourself. That means you need a "callback hook". A function parameter that you pass when creating the handler. You store that function reference in a local variable, and call it in the "catch", pass the exception to it. Not the "catch" in my answer -- the one where the exception occurs. Too tired to come up with the code right now.
  • ridoy
    ridoy almost 11 years
    @ToolmakerSteve-Welcome..:)
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    Totally rewritten, now that I am awake :)
  • John Saunders
    John Saunders almost 11 years
    -1: I see no value in a custom exception: the Exception class already has an InnerException property. Also, you have shown no reason to even catch the exception.
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    @John Saunders .. And thats sufficient reason to -1 a lengthy, multi-part answer, rather than proposing an edit? You are rude. And "I have shown no reason to catch the exception"? Irrelevant. The point of SO is to give people options.
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    @John Saunders No, you only have time to be rude.
  • John Saunders
    John Saunders almost 11 years
    You're welcome to your opinion. My opinion was "-1". I'll change my opinion once you've adequately edited your answer.
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    OK, I'll explain. The reason to make a custom exception, is so that your app handler is better informed as to why the exception happened. Without this, the app handler only knows WHAT exception happened. With this, it knows WHERE it happened. I already stated this reason in the opening paragraph of my answer "You want to inform it that this exception happened as part of processing ReceiveCompleted." If someone does not desire that behavior, then they are not going for choice C. My answer stands as is.
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    Recall the OP already had a behavior that was throwing to app level. He wanted something more controlled. These are THREE different ways to intercept the Exception, that ALL can be aware that it happened in ReceiveCompleted. This is what the OP asked for. Do you disagree?
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    @JohnSaunders I quote from help stackoverflow.com/help/privileges/vote-down "The up-vote privilege comes first because that's what you should focus on: pushing great content to the top. Down-voting should be reserved for extreme cases. It's not meant as a substitute for communication and editing. Instead of voting down: ... If something is wrong, please leave a comment or edit the post to correct it." IMHO, this makes for a friendlier site, that encourages people to answer questions.
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    ... And here is a lengthy discusssion giving people's views on when downvoting is appropriate meta.stackexchange.com/questions/2451/… tvanfosson says what I feel most succinctly "As a general rule I try not to downvote unless I feel the answer is actively unhelpful."
  • John Saunders
    John Saunders almost 11 years
    Custom exceptions should only be used when it is expected that a caller will take different actions depending on the exception. If you just want a different Message, throw an existing exception.
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    @JohnSaunders "when it is expected that a caller will take different actions depending on the exception." I agree. In this case, the "different action" is that the app-handler has more options being told is in RC than if all they know is that Exception XYZ occurred. Without this, they will likely have no better alternative than to kill the process, log the error, restart the process. Knowing that the trouble is in RC, the author has more options. IF the OP had been satisfied with brute force, "something went wrong, so kill me", they wouldn't have postedQ
  • John Saunders
    John Saunders almost 11 years
    Options are worthless if they are not used.
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    Huh? Its up to me to intuit why OP wanted to specifically direct an exception in RC to particular code? Not. What I did is showed what test to put where "catch (RCException)". What OP puts inside that block is entirely up to him.
  • ToolmakerSteve
    ToolmakerSteve almost 11 years
    @JohnSaunders I have made two edits to attempt to address your concerns. Does this accomplish your goal of having a 100% accurate answer? (1) I've added comment to the spot in Approach C that handles the custom RCException, to clarify the benefit of that catch test in app-handler. (2) I've added Approach D that doesn't create a custom exception, and stated that this is appropriate, if you don't need to know it was RC that caused the problem. For D, I've linked to an existing SO post, that describes app-level catching.
  • Tim Windsor
    Tim Windsor almost 11 years
    @toolmakersteve thank you for your time on this. your going to hate this! I think I understand what you're proposing, but I can't relate it to the MSMQ.ReceiveCompleted event. The problem I am having is that an error is thrown from the _azuresql.writeMessageToStorage() (someone elses dll) not on the receiveCompleted event. so when the delegate receives a 'thrown' error, how do I pass it back to the caller.... Please forgive me if I'm misunderstanding how to implement your examples.. Thanks again..