Исключение не попадает при первом улове, а вместо этого обрабатывается уловом верхнего уровня - PullRequest
0 голосов
/ 08 февраля 2020

У меня есть класс с именем SearchProbe, потому что я пишу юнит-тесты. Один модульный тест предназначен для проверки способности основного метода обработки моего класса (называемого RunSearchProbe) уметь правильно отвечать на CancellationTokens. Основной метод обработки моего класса выполняет асинхронные c субметоды, которые все выбрасывают OperationCanceledException, когда CancellationToken отменяется. Затем в моем основном методе RunSearchProbe я пытаюсь перехватить это исключение и ответить на него.

Проблема: Проблема в том, что по какой-то причине OperationCanceledException НЕ выполняется перехватил основной метод RunSearchProbe, и он доходит до стека вызовов моего модульного теста для обработки, и я не знаю почему?!

Вот мой основной класс:

public class SearchProbe
{
    protected async Task RunSearchProbe(CancellationToken cancellationToken) {
        try
        {
            try
            {
                using (cancellationToken.Register(() => {
                    //some code here
                }))
                {
                    Task<bool> initTask = Initialize(cancellationToken);
                    await initTask;

                    //some code here
                }
            }
            catch (Exception exception) when (exception.GetType().Equals(typeof(OperationCanceledException))
            || exception.InnerException.GetType().Equals(typeof(OperationCanceledException)))
            {
                //some code here // -------->>> (Point 1) This is where the OperationCanceledException SHOULD get caught
            }
            finally
            {
                //some code here
            }
        }
        catch (Exception e)
        {
            //some code here  // -------->>> (Point 2) ... Or AT LEAST get caught here
        }
    }

    private async Task<bool> Initialize(CancellationToken cancellationToken) {

        try
        {
            using (cancellationToken.Register(() => {
                throw new OperationCanceledException();
            }))
            {
                //some code here
                return true;
            }
        }
        catch (Exception exception)
        {
            //some code here
        }
    }
}

Это унаследованный класс:

class MockSearchProbe : SearchProbe
{   
    static MockSearchProbe()
    {
        //some code here
    }

    public async Task RunProbeManually()
    {
        try {

        CancellationTokenSource cts = new CancellationTokenSource();

        Task probeTask = RunSearchProbe(cts.Token);

        cts.Cancel();

        await probeTask;

        }
        catch (Exception exception) when (exception.GetType().Equals(typeof(OperationCanceledException))
              || exception.InnerException.GetType().Equals(typeof(OperationCanceledException)))
        {
            //do something (Point 3) ... But it actually gets caught here for some reason
        }
    }
}

Это тестовый класс:

[TestClass]
public class SearchProbeTests
{

    [TestMethod]
    public async Task TestProbe_Cancellation()
    {
        MockSearchProbe probe = new MockSearchProbe();

        Task result = probe.RunProbeManually();

        await result;
    }
}

Пожалуйста, см. Шаги 1, 2 и 3, прокомментированные выше, чтобы понять, что я имею в виду. .. Почему блок catch внутри метода RunSearchProbe моего основного класса НЕ перехватывает OperationCanceledException ??

1 Ответ

0 голосов
/ 09 февраля 2020

Документация для CancellationToken.Regsiter гласит, что метод:

Регистрирует делегата, который будет вызван при отмене этого CancellationToken.

Исходя из этого описания, я ожидаю, что обратный вызов регистрации, определенный в методе Initialize, должен выполняться при вызове cts.Cancel() в RunProbeManually. Исключение не создается и не генерируется до этой точки, которая находится в области действия блока try / catch с меткой «Точка 3».

Вот упрощенная иллюстрация:

using System;

class MainClass {
  public static void Main (string[] args) {

    Action throwException = null;

    try {
      Console.WriteLine("Defining delegate");

      throwException = () => {
        Console.WriteLine("Throwing exception");
        throw new Exception();
      };

    } catch (Exception) {
      Console.WriteLine("Exception caught at point 1");
    }

    try {
      Console.WriteLine("Invoking delegate");

      throwException.Invoke();
    } catch (Exception) {
      Console.WriteLine ("Exception caught at point 2");
    }
  }
}

Вывод:

Defining delegate
Invoking delegate
Throwing exception
Exception caught at point 2
...