Является ли общее исключение поддерживаемым внутри catch?

Вышеупомянутый блок, ссылающийся на другой вопрос + ответ на SO, НЕ содержит правильный ответ, который здесь применяется!

У меня есть метод, используемый для модульного тестирования. Цель этого метода состоит в том, чтобы убедиться, что часть кода (на который ссылается делегат) выдает конкретное исключение. Если это исключение выбрано, выполняется unit test. Если исключение не выбрасывается или не генерируется исключение другого типа, unit test завершится с ошибкой.

/// <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.");
}

Следующий вызов должен быть успешным, но он не работает:

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

Когда вызывается ожидаемое исключение типа TException, оно всегда захватывается вторым уловом, а не первым уловом. Почему это?

Конечно, я могу использовать workarround с одним catch и test, если ex имеет тип TException. Я просто хочу знать/понимать, почему этот код компилируется, но простой (никогда?) Работает.

ИЗМЕНИТЬ

По запросу "рабочая" демонстрация:

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!!!");
            }
        }
    }
}

Ответ 1

Вы не первый человек, столкнувшийся с этой проблемой. Этот вопрос очень похож. Если вы просматриваете ответы и ссылки, это сводится к ошибке в CLR.

EDIT: В качестве продолжения я выполнил пример Martin от VS2010 и получил следующие результаты:

  • Назначение .NET 4, PASS
  • Targetting.NET 3.5, FAIL
  • Targetting.NET 3.5 в режиме RELEASE, PASS

К сожалению, все ссылки SO на Microsoft Bug Report уже мертвы, и я не смог найти других.

Ответ 2

(Это не конкретный ответ, но я не мог опубликовать его как комментарий тоже.)

Я не могу воспроизвести это (VS 2012,.NET 4.5, С# 5.0, каждый установленный SP).

Я определил этот класс исключения:

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

и метод:

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"); }
}

и я протестировал его следующим образом:

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

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

а выход:

  • получил Draft.MyException
  • получил System.DivideByZeroException

Итак (IMHO) вы должны искать где-то еще.