Исключение обработки с лямбда-асинксом, исключение не получено

0
задан nihulus 13 July 2018 в 12:25
поделиться

2 ответа

Вот мое решение, которое работает как предпочтительное:

var aaa = null;
await ExcecuteWithLogging(async () => 
                aaa = await Method());


 public async Task<string> ExcecuteWithLogging(Func<Task> action)
 {
        try
        {
            await action();
        }
        catch (Exception ex)
        {
            // handle exception

            return null;
        }

        return "ok";
 }
0
ответ дан nihulus 17 August 2018 в 12:53
поделиться
  • 1
    Вы почти там! Что означает @Servy, не синхронно ожидая, что вместо вызова Wait() просто напишите await action(), так как действие уже есть Func<Task>. Этот ответ Стивена Клири поможет вам понять это – taquion 25 July 2018 в 15:55
  • 2
    Кстати, это решение было тем, что я предлагал ... написать перегрузку, которую вы пишете, но ждать, а не ждать. Кажется, что @Servy может знать еще один способ сделать это без перегрузки, принимающего Func<Task> и имеющего только Func<T> в качестве параметра, но это то, о чем вы должны его спросить ... – taquion 25 July 2018 в 15:58
  • 3
    @taquion Отличное объяснение из вашей ссылки, в основном я должен использовать async от начала до конца. – nihulus 26 July 2018 в 07:31
  • 4
    И не вызывайте Task.Run .. вы можете сделать просто await action(), так как действие уже есть Func<Task>. знак равно – taquion 26 July 2018 в 18:54

Проблема заключается в том, что вы не реализуете перегрузку ExecuteWithLogging, которая решается специально для Func<Task<T>>. Помните, что лямбда-выражения могут быть преобразованы в любой совместимый делегат, поэтому ваша асинхронная лямбда успешно присваивается вашему текущему методу ExecuteWithLogging.

Прежде чем вы прочитаете остальную часть просьбы о ответе, посмотрите на после статьи , это очень помогло мне, когда я столкнулся с той же проблемой, с которой вы сейчас сталкиваетесь. Я почти уверен, что после прочтения этой статьи вы поймете, что здесь происходит, но для полноты здесь у вас есть образцы, которые сделают вещи более ясными. Я изменил ваши методы следующим образом:

public static async void SearchByPersonnummerAsync(string personnummer)
{
    var aaa = await ExcecuteWithLogging(async () =>
    {
        Console.WriteLine("Executing function");
        var res = await Task.Run(() =>
        {
            Thread.Sleep(100);
            Console.WriteLine("Before crashing");
            throw new Exception();
            return 1;
        });

        Console.WriteLine("Finishing execution");
        return res;
    });

}

private static T ExcecuteWithLogging<T>(Func<T> function)
{
    try
    {
        Console.WriteLine("Before calling function");
        function();
        Console.WriteLine("After calling function");
    }
    catch (Exception ex)
    {
        var message = ex.Message;
        Console.WriteLine(message);
    }

    Console.WriteLine("Returning..");
    return default(T);
}

Это именно то, где вы сейчас находитесь, я добавил только некоторый код ведения консоли, какой результат?

Как вы можете видеть, ExecutingWithLogging возвращается, когда делегат async даже не разбился!

Давайте добавим перегрузку, которая принимает Func<Task<T>>

private static async Task<T> ExcecuteWithLogging<T>(Func<Task<T>> function)
{
    T result;
    try
    {
        Console.WriteLine("Before calling function");
        result =  await function();
        Console.WriteLine("After calling function");

    }
    catch (Exception ex)
    {
        var message = ex.Message;
        Console.WriteLine(message);
        return default(T);

    }

    Console.WriteLine("Returning..");
    return result;
}

Компилятор теперь выберет это из статьи, упомянутой выше:

Компилятор предпочитает вызов метода, ожидающий делегата, который возвращает задачу. Это хорошо, потому что это тот, который вы предпочитаете использовать. Компилятор приходит к такому выводу, используя правила вывода типа о возвращаемом значении анонимных делегатов. Предполагаемый тип возвращаемого значения любой асинхронной анонимной функции считается заданием. Зная, что анонимная функция, представленная лямбдой, возвращает задачу, перегрузка Task.Run (), которая имеет аргумент Func, является лучшим совпадением.

Теперь вывод:

И вы получите доступ к выходу. Итак, что такое мораль? Опять же, я цитирую указанную статью:

Во-первых, избегайте использования async lambdas в качестве аргументов методам, ожидающим действия, и не предоставляйте перегрузку, ожидающую Func. Если вы это сделаете, вы создадите асинхронную lambda. Компилятор с радостью предположит, что это то, что вы хотите.

Во-вторых, если вы создаете методы, принимающие делегаты в качестве аргументов, подумайте, могут ли программисты использовать async лямбда в качестве этого аргумента. Если это так, создайте перегрузку, которая использует Func в качестве аргумента в дополнение к Action. В качестве следствия создайте перегрузку, которая принимает Func> в дополнение к Task для аргументов, которые возвращают значение.

EDIT. Как @Servy указывает на комментарии, если вы используете только оригинальную версию ExecuteWithLogging T на самом деле является Task<Aaa> ... но единственный способ, который я могу ожидать в ожидании без перегрузки:

private static async Task<T> ExcecuteWithLogging<T>(Func<T> function)
{
    try
    {

        var result =  function();
        if (result is Task t)
        {
            return await t;
        }

        return result;
    }
    catch (Exception ex)
    {
        var message = ex.Message;
        Console.WriteLine(message);
    }

    Console.WriteLine("Returning..");
    return default(T);
}

Но, кроме того, что он уродливый ИМХО, он всегда терпит неудачу, поскольку вызов function() выполняется синхронно и к тому моменту, когда вы хотите дождаться, что задача уже завершена или сработала. Но худшая часть этого подхода заключается в том, что он даже не компилируется, компилятор жалуется на: Не может неявно преобразовывать тип void в T

-1
ответ дан taquion 17 August 2018 в 12:53
поделиться
  • 1
    Лямбда не преобразуется в Action. Он преобразуется в Func<Task<Aaa>>. Просто ничего не наблюдается в результате возвращаемой задачи. – Servy 23 July 2018 в 13:39
  • 2
    @Servy Я не сказал, что он был преобразован в Action, статья является просто ссылкой. В любом случае, если вы не добавляете перегруженный метод, вы не можете наблюдать результат, поскольку T не является Func<Task<Aaa>>, попробуйте дождаться function() .. он не компилируется .. – taquion 23 July 2018 в 13:57
  • 3
    Но T является a Task<Aaa>, что означает, что метод есть , принимающий Func<Task<Aaa>>. Его просто не ждут. – Servy 23 July 2018 в 14:01
  • 4
    О, и ваш ответ does говорит, что делегат не разрешает Func<Task<Aaa>>. Это буквально ваше вступительное предложение. Это неправильно, это делает для решения именно этого. – Servy 23 July 2018 в 14:06
  • 5
    @Servy спасибо за указание на это, я хотел сказать, что OP должен написать метод, который решает конкретно Func<Task<Aaa>>, я отредактирую свой ответ, добавив это наречие. Я знаю, что T на самом деле Task<Aaaa> ... как бы вы ожидали его без перегрузки? – taquion 23 July 2018 в 14:14
Другие вопросы по тегам:

Похожие вопросы: