12

The above block refering to another question+answer on SO does NOT contain a correct answer which applies here!

I have a method used for unit testing. The purpose of this method is to ensure that a piece of code (refered to by a delegate) will throw a specific exception. If that exception is thrown, the unit test succeeds. If no exception is thrown or another type exception is thrown, the unit test will fail.

/// <summary>
/// Checks to make sure that the action throws a exception of type TException.
/// </summary>
/// <typeparam name="TException">The type of exception expected.</typeparam>
/// <param name="action">The code to execute which is expected to generate the exception.</param>
public static void Throws<TException>(Action action) 
    where TException : Exception
{
    try
    {
        action();
    }
    catch (TException)
    {
        return;
    }
    catch (Exception ex)
    {
        Assert.Fail("Wrong exception was thrown. Exception of type " + ex.GetType() + " was thrown, exception of type " + typeof(TException) + " was expected.");
    }
    Assert.Fail("No exception was thrown. Exception of type " + typeof(TException) + " was expected.");
}

The next call should succeed, but it fails:

int result = 0;
Throws<DivideByZeroException>(() => result = result / result);

When the expected exception of type TException is thrown, it is always caught by the second catch, not by the first catch. Why is this?

Of course I can use a workarround with one catch and test if ex is of type TException. By I simply want to know/understand why this code compiles but simple (never?) works.

EDIT

At request a "working" demo:

using System;

namespace GenericExceptionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            int n = 0;
            Catch<DivideByZeroException>(() => n = n / n);
        }

        static public void Catch<TException>(Action action)
            where TException: Exception
        {
            try
            {
                action();
                Console.WriteLine("No exception thrown. !!!Fail!!!");
            }
            catch (TException)
            {
                Console.WriteLine("Expected exception thrown. PASS!");
            }
            catch(Exception ex)
            {
                Console.WriteLine("An unexpected exception of type " + ex.GetType() + " thrown. !!!FAIL!!!");
            }
        }
    }
}
Martin Mulder
  • 12,642
  • 3
  • 25
  • 54
  • 7
    What unit test framework are you using? Most already have `Assert.Throws`... – Jon Skeet Jun 05 '13 at 10:40
  • 8
    Your code works for me... please show a short but complete program demonstrating the problem. – Jon Skeet Jun 05 '13 at 10:42
  • Why to state that a wrong type of exception is thrown? Maybe .Net framework itself throws you a System.Drawing.Exception when you are working with GUIs? – Mike de Klerk Jun 05 '13 at 10:44
  • I'll never understand why these kind of "my machine is possessed by demon" questions get so many votes. Throw the machine out of the window, get another one. Done. – Hans Passant Jun 05 '13 at 11:04
  • 1
    @Jon Skeet: Forget about the unittest-story... this is a question about try-catch. – Martin Mulder Jun 05 '13 at 11:16
  • Well the code you've given works for me, which eradicates the purpose of the question, to be honest. If you throw out the unit testing side of things it should be *even easier* to show a short but complete program demonstrating the problem. – Jon Skeet Jun 05 '13 at 11:17
  • @Jon Skeet: I added the tag: VS2008. It seems to be a problem only in VS2005 and VS2008. There are similar problems on SO, with some links to microsoft, but none of those links are working. I am trying to find an official page where this bug is acknowledged. The best SO-corresponding page is: http://stackoverflow.com/questions/700935/why-does-catchtexception-handling-block-behaviour-differ-under-the-debugger-af – Martin Mulder Jun 05 '13 at 11:25
  • @Hans Passant: Because this is a real-life-problem on real-life-machines? And so it turns out, it seems to be a bug inside the CLR and/or VS2008. Why would questions about bugs inside C# / .NET / CLR not be valid questions? – Martin Mulder Jun 05 '13 at 11:29
  • @Jon - What version of the CLR are you targetting there? Does it still work if you switch down to target 3.5 client? – Chris Spicer Jun 05 '13 at 11:29
  • 1
    @ChrisSpicer: I was targeting .NET 4.5, but I'm happy to try others... once we've got a short but complete program which demonstrates the problem for the OP. Currently we just have snippets, and I'd much rather know that I've got *exactly* the same complete app. – Jon Skeet Jun 05 '13 at 11:31
  • 1
    possible duplicate of [Why can't I catch a generic exception in C#?](http://stackoverflow.com/questions/1577760/why-cant-i-catch-a-generic-exception-in-c) – H H Jun 05 '13 at 11:56
  • @Jon Skeet: A demo is added. – Martin Mulder Jun 05 '13 at 12:30
  • I'm getting the expected behaviour even when running against a .NET 2.0 CLR - `Environment.Version` shows 2.0.50727.6400. – Jon Skeet Jun 05 '13 at 12:39
  • @HansPassant: you're speaking with aphorisms. Added to my quotation notebook. :) – Dennis Jun 05 '13 at 13:37

2 Answers2

2

You're not the first person to encounter this problem. This question is very similar. If you dig through the answers and links, it comes down to a bug in the CLR.

EDIT: As a follow up, I've run Martin's example from VS2010 and got the following results:

  • Targetting .NET 4, PASS
  • Targetting .NET 3.5, FAIL
  • Targetting .NET 3.5 in RELEASE mode, PASS

Sadly, all the SO links to the Microsoft Bug Report are dead now and I haven't been able to find any others.

Community
  • 1
  • 1
Chris Spicer
  • 2,144
  • 1
  • 13
  • 22
  • 1
    That previous question was answered back in 2009, does it still apply? – Patrick Magee Jun 05 '13 at 11:02
  • Good question. I just put Martin's code in VS (with minor tweaks). If I target .NET 4, the code works as expected. If I target 3.5, I see the odd behaviour that Martin described. – Chris Spicer Jun 05 '13 at 11:19
  • It does not apply. Both questions are similar, but the other question has an extra exception variable `tex`, and the (correct) answer is to remove that variable. In my case I am not using a variable, so that answer cannot apply to this situation. – Martin Mulder Jun 05 '13 at 11:20
  • @ChrisSpicer: VS 2010, Win7 x64, Windows Update is turned on (hence, I've got all hotfixes and SP's, if they were published). I've tried 4, 3.5 and 2.0 targets (2.0 with additional declaring of `Action`, of course). It works as expected (falls into `catch (TException)`). What am I doing wrong? – Dennis Jun 05 '13 at 13:28
0

(This is not a concrete answer; but I could not post it as a comment too.)

I can not reproduce this (VS 2012, .NET 4.5, C# 5.0, every SP installed).

I defined this exception class:

class MyException : Exception
{
    public MyException() { }
    public MyException(string message) : base(message) { }
}

and a method:

static void Throws<T>(Action action) where T : Exception
{
    try
    {
        action();
    }
    catch (T) { Console.WriteLine("got {0}", typeof(T)); }
    catch (Exception) { Console.WriteLine("got Exception"); }
}

and I have tested it this way:

Throws<MyException>(() => { throw new MyException(); });

int result = 0;
Throws<DivideByZeroException>(() => result = result / result);

and the output is:

  • got Draft.MyException
  • got System.DivideByZeroException

So (IMHO) you should look somewhere else.

Kaveh Shahbazian
  • 13,088
  • 13
  • 80
  • 139