1

In order to streamline my try blocks, I've made a static function to handle error catching because I was already catching errors the same way in all my methods anyway.

static class Exceptional
{
    public static Exception Try(Action action, [CallerMemberName] string cmn = "")
    {
        try
        {
            action();
            return null;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace, cmn + ": " + ex.GetType().Name);
            return ex;
        }
    }
}

And then I've replaced all cases of

        try
        {
            ...
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace);
        }

with

        Exceptional.Try(() =>
        {
            ...
        });

This not only streamlines wrapping my code in try blocks but also gives me a centralized place to globally change the way I'm handling exceptions.

I know it's not good practice to catch all exceptions and treat them all the same. I understand I'm only supposed to catch the types of exceptions I'm expecting to be thrown, but right now I'm just testing things and I don't know what's going to be thrown so I've been using try blocks to make sure I don't get silent or unexplained failures. And that's not what my question is about.

I'd like to know if I'm in danger of changing my code's behavior by doing this. Obviously the call stack is being changed by inserting an additional function into it, but can this extra function potentially cause unexpected issues? Critiques are welcome! Thank you.

EDIT: I should mention that I'm currently wrapping entire method bodies in my Exceptional.Try calls, so I'm particularly interested in knowing if I might encounter strange behavior in that case rather than if something outside the lambda expression might cause strange behavior.

Kyle Delaney
  • 11,616
  • 6
  • 39
  • 66
  • "I've been using `try` blocks to make sure I don't get silent failures" - What??? If you don't use exception handling at all then you get to see exceptions - using `try` blocks allows for silent failures. You should only catch exceptions that you can recover from and that you can meaningfully handle. You should almost never do `catch (Exception)`. – Enigmativity May 18 '17 at 00:17
  • Okay. I do get silent failures sometimes. If you open Exception Settings you can choose which exceptions it breaks on, so maybe that has something to do with it. But anyway, silent failures aren't the only issue. I also want to control what information I'm given from the exception. – Kyle Delaney May 18 '17 at 00:42
  • Oh, you know what's going on? It's not a silent failure per se, but the program just crashes without saying why. – Kyle Delaney May 18 '17 at 00:51
  • You can put application level exceptions to catch everything. It's just a waste of your time to put `try` blocks everywhere. Have a read of this: https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/ – Enigmativity May 18 '17 at 01:12
  • Oh okay, thank you. – Kyle Delaney May 18 '17 at 01:38
  • Here's an example of an application failing silently if I don't catch the exception: http://stackoverflow.com/questions/44051514/combobox-crashing-wpf-application-because-selectionchanged-is-firing-too-soon – Kyle Delaney May 18 '17 at 15:09

2 Answers2

1

That should work fine. As you alluded to, the extra function call will show up in the call stack, but that should hardly be confusing to anyone.

A couple suggestions... the ToString() method returns all of the info you're looking for as well as any inner-exceptions. And I don't see a need to return the exception type unless you want to.

public static void Try(Action action, [CallerMemberName] string cmn = "")
{
    try
    {
        action();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
}
Grant Winney
  • 65,241
  • 13
  • 115
  • 165
1

It will work. It will also change the way your code behaves. Here's a test method:

[TestMethod]
public void TestMethod1()
{
    int output = 0;
    Exceptional.Try(() => output = 2 + 2);
    Assert.AreEqual(4, output);
}

But here are some of the problems you get with that:

Code execution doesn't automatically stop if an exception is thrown. For example, you could do this:

Exceptional.Try(() => DoSomethingThatThrowsAnException());

And unless you declare another variable to hold the return value (exception or null) you don't know if anything went wrong (assuming the MessageBox is just for testing.) So it takes the predictable behavior of C# and turns it into something unpredictable. Someone would have to inspect that class to see what's happening when they execute their method. So you're now writing a try/catch but you have to follow a new convention, checking the return value of the exception. And unlike the default behavior, if you forget to check then your exceptions are never raised.

I had to declare the output variable separately and initialize it, even though I'm going to assign another value to it later. That's because I can't just do var output = 2 + 2. If I just declare it without initializing then when I try to use the value later the compiler complains that the variable may not be initialized. That's because it doesn't know whether the action is executing immediately or later (or ever.)

The default behavior of exceptions and try/catch is good and everyone understands it, so I wouldn't recommend doing anything to change it. If an exception happens then execution should be transferred to an exception handler or raised to the calling method.

I recommend taking a look at interceptors. They provide a way to keep exception handling separate from your code.

Community
  • 1
  • 1
Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • True - I should clarify - the declaration and assignment would happen on the same line, but now the method doesn't return a value. – Scott Hannen May 18 '17 at 00:04
  • I mean the whole thing is just for testing. So of course code execution would stop because the message box is shown. – Kyle Delaney May 18 '17 at 00:49
  • It would stop but then it would continue after the message box. If a line of code throws an exception then the next line shouldn't execute. – Scott Hannen May 18 '17 at 00:51
  • Oh I see. Anyway, the way I'm doing it now is wrapping entire method bodies in the try blocks. I know that's not the way it's supposed to work, but again this is just a test suite for myself and not a code base that's being shared or used for a product. – Kyle Delaney May 18 '17 at 00:55