Throw and preserve stack trace not as expected as described by Code Analysis
Asked Answered
T

3

8

Doing a code analysis gave me item CA2200:

CA2200 Rethrow to preserve stack details 'func()' rethrows a caught exception and specifies it explicitly as an argument. Use 'throw' without an argument instead, in order to preserve the stack location where the exception was initially raised.

I have implemented the suggestion, but I seem to get the same stack trace regardless.

Here is my test code and output (the white space is intended to give obvious line numbers):

Expected error at Line 30

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace throw_test
{
    class Program
    {
        static void Main(string[] args)
        {

            try
            {
                try
                {


                    // line 22 below
                    throw new Exception();
                }
                catch (Exception ex) // DEFINES AND THROWS 'ex'
                {



                    // line 30 below
                    throw ex;
                }
            }
            catch (Exception ex)
            {
                Console.Write(ex.StackTrace);
            }

            Console.Read();
        }
    }
}

Output

at throw_test.Program.Main(String[] args) in c:\Users\Richard\Documents\Visual Studio 2013\Projects\using_throw_closestream\using_throw_closestream\Program.cs:line 30

Expected error at line 22

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace throw_test
{
    class Program
    {
        static void Main(string[] args)
        {

            try
            {
                try
                {


                    // line 22 below
                    throw new Exception();
                }
                catch (Exception) // NO 'ex'
                {



                    // line 30 below
                    throw;
                }
            }
            catch (Exception ex)
            {
                Console.Write(ex.StackTrace);
            }

            Console.Read();
        }
    }
}

Output

at throw_test.Program.Main(String[] args) in c:\Users\Richard\Documents\Visual Studio 2013\Projects\using_throw_closestream\using_throw_closestream\Program.cs:line 30

The same result.

My question

With other such questions suggesting more complex or explicit methods to preserve the stack trace, why does the Code Analysis suggestion ask me to do something which seems not to work?

If this suggestion is in fact correct, what am I doing wrong?

Townspeople answered 22/11, 2013 at 15:0 Comment(4)
you still want to catch the exception, just dont throw itRendering
My question is more to do with why the suggestion seems to be wrong, rather than whether to rethrow or not.Townspeople
I think the suggestion is only relevant when you throw and catch over multiple methods. So method A throws, methods B catches and rethrows. If B does 'throw ex' the stackstrace will start at B, if it just does 'throw' it will start at A.Perspicuity
This is actually a limitation in the thread exception handling plumbing inside the CLR. It piggy-backs on top of Windows SEH support. Which is stack frame based, there is only one for a method. You always lose the original throw location in your sample code. Throw the exception from a method you call to see the difference.Parrot
T
5

Mark above is correct.

The stack trace is only preserved when thrown from another method.

The stack trace here will be preserved by including the original error being thrown from Method()

The following code illustrates this:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace throw_test
{
    class Program
    {
        static void Main(string[] args)
        {

            try
            {
                try
                {


                    // line 22 below
                    Method();
                }
                catch (Exception ex)
                {



                    // line 30 below
                    throw;
                }
            }
            catch (Exception ex)
            {
                Console.Write(ex.StackTrace);
            }

            Console.Read();
        }

        public static void Method()
        {
            throw new Exception();
        }
    }
}

Output

at throw_test.Program.Method() in c:\Users\Richard\Documents\Visual Studio 2013\Projects\using_throw_closestream\using_throw_closestream\Program.cs:line 43 at throw_test.Program.Main(String[] args) in c:\Users\Richard\Documents\Visual Studio 2013\Projects\using_throw_closestream\using_throw_closestream\Program.cs:line 30

Townspeople answered 22/11, 2013 at 15:14 Comment(0)
B
7

If you are using .Net 4.5, you can use ExceptionDispatchInfo.Capture(ex).Throw(); and you will get a nice stack trace including line 22 and line 30.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {

            try
            {
                try
                {


                    // line 22 below
                    throw new Exception();
                }
                catch (Exception ex) // NO 'ex'
                {



                    // line 30 below
                    ExceptionDispatchInfo.Capture(ex).Throw();
                }
            }
            catch (Exception ex)
            {
                Console.Write(ex.StackTrace);
            }

            Console.Read();
        }
    }
}

Output

   at ConsoleApplication1.Program.Main(String[] args) in c:\ConsoleApplication1\Program.cs:line 22
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at ConsoleApplication1.Program.Main(String[] args) in c:\ConsoleApplication1\Program.cs:line 30
Boman answered 23/3, 2015 at 18:51 Comment(0)
T
5

Mark above is correct.

The stack trace is only preserved when thrown from another method.

The stack trace here will be preserved by including the original error being thrown from Method()

The following code illustrates this:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace throw_test
{
    class Program
    {
        static void Main(string[] args)
        {

            try
            {
                try
                {


                    // line 22 below
                    Method();
                }
                catch (Exception ex)
                {



                    // line 30 below
                    throw;
                }
            }
            catch (Exception ex)
            {
                Console.Write(ex.StackTrace);
            }

            Console.Read();
        }

        public static void Method()
        {
            throw new Exception();
        }
    }
}

Output

at throw_test.Program.Method() in c:\Users\Richard\Documents\Visual Studio 2013\Projects\using_throw_closestream\using_throw_closestream\Program.cs:line 43 at throw_test.Program.Main(String[] args) in c:\Users\Richard\Documents\Visual Studio 2013\Projects\using_throw_closestream\using_throw_closestream\Program.cs:line 30

Townspeople answered 22/11, 2013 at 15:14 Comment(0)
T
1

In C# 6.0 you can use exception filters to perform some logic (for example logging) without need for explicit rethrow, it will of course preserve your stack trace.

Description can be found here in Exception filters section. It says:

It is also a common and accepted form of “abuse” to use exception filters for side effects; e.g. logging. They can inspect an exception “flying by” without intercepting its course. In those cases, the filter will often be a call to a false-returning helper function which executes the side effects.

Trude answered 9/9, 2015 at 11:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.