tags:

views:

1676

answers:

8

If all I want to do is throw the exception up a level when it occurs?

private void TryCatch()
{
   try
   {
       foo();
   }
   catch (Exception ex)
   {
       throw;
   }
}

private void NoTryCatch()
{
   foo();
}

Aren't these two methods the same?

If an exception occurs in TryCatch it will be thrown up a level and if an exception occurs in NoTryCatch, the exception will also be thrown up a level.

This question came about after using ReSharper and noticing that it suggested to remove the try/catch block as it was redundant.

+1  A: 

These two methods are essentially the same. In this case ReSharper was correct with its suggestion.

Otávio Décio
+3  A: 

No, you do not need the try catch. The only reason you would want to use the first function is if you wanted to do some logging or release resources before handling the function further up.

amdfan
A: 

ReSharper is correct. Unless you actually intend to catch and do something about an exception there is no point including a try..catch block.

AnthonyWJones
Unless you need the finally block to do some cleanup work.
Fred Clown
@Fred: That would be `try..finally` then not a `try..catch`, right?
AnthonyWJones
A: 

Yes, the try catch block is redundant. The stack will just be unwound until a try/catch is found to handle the exception.

Kevin Loney
A: 

If all you do is rethrow the exception you don't need the try/catch block. Generally you should only catch exceptions when you can handle them. Otherwise let them propagate upwards.

Brian Rasmussen
+12  A: 

Yes, those methods are pretty much (*) the same. The only difference is that it's easy to put a breakpoint in the first one. I'd always go with the second unless I really needed to break there and only there (as opposed to immediately any exceptions of that type were thrown, which would be easy). Even if I ever used the first, I'd put it back to the second form before committing the code.

(*) There may well be some differences in terms of how the JIT handles them. The first will end up with more IL, which will affect opportunities for inlining etc.

EDIT: I can't resist a bit of micro-benchmarking. It looks like try/catch/throw has nastier effects on performance than just disabling inlining:

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

public class Test
{
    const int Iterations = 1000000000;

    static void Main()
    {
        Stopwatch sw;

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            SimpleMethod();
        }
        sw.Stop();
        Console.WriteLine("Simple method: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            NoInlining();
        }
        sw.Stop();
        Console.WriteLine("No inlining: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            TryCatchThrow();
        }
        sw.Stop();
        Console.WriteLine("try/catch/throw: {0}", sw.ElapsedMilliseconds);
    }

    static void SimpleMethod()
    {
        Foo();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void NoInlining()
    {
    }

    static void TryCatchThrow()
    {
        try
        {
            Foo();
        }
        catch (Exception)
        {
            throw;
        }
    }

    static void Foo() {}
}

Compile with /o+ /debug-

Results (three runs):

Simple method: 504, 495, 489
No inlining: 2977, 3060, 3019
try/catch/throw: 5274, 4543, 5145

Jon Skeet
I believe any try/catch will prevent inlining. Researching ...
JaredPar
That was my guess too, though I didn't have the energy for research. Not that there's anything to stop the JIT from noticing that the try/catch is redundant in this case, I suspect, and stripping it...
Jon Skeet
Can't find anything to back up my assertion. I believe it was either throwing or try/catch prevents inline. I suspect it may actually be throw vs. catch.
JaredPar
nastier effects?(5274-504)/1000000000 = no effect when you start doing something in foo()
iik
@iik: When you start doing anything particularly significant in Foo(), inlining will go away anyway. However, in some cases it *can* be significant. If it weren't worth doing, MS wouldn't make the JIT capable of inlining, would they?
Jon Skeet
A: 

Hi!

Something I thought about, but I wasn't sure 100% so I went to check. I was right, sometimes.

Apparently, if you re-throw the exception, which is what your code is doing, you could end up changing the stack-trace. First of all, if you were to write throw ex; that would reset the stack-trace. Second, even when writing throw; there can also be cases where information is missing. See this article and some user comments following up on it.

Of course, most of these issues are related to the stack-trace and line-numbers, which are important, but I thought it would also affect performance, not just because of inlining (or the lack thereof), but also because of the whole exception catching and throwing overhead, but I didn't find anything concrete on this.

Schmuli
+1  A: 

They really are not the same. Throw on its own or throw ex mess with the stack trace information and can make debugging harder.

The best reason to catch an exception is to add context to the stack trace, like:

try {
  Foo();
}
catch (Exception e) {
  throw new BetterException("I did something slightly silly", e);
}
ewalshe