C# 如何正确等待异步委托?

C# 如何正确等待异步委托?,c#,asynchronous,async-await,delegates,C#,Asynchronous,Async Await,Delegates,在同步世界中,我有一个TryExecute函数来包装try/catch/log逻辑以供重用,如下所示: TryExecute(() => SyncFunction()); private static void TryExecute(Action action) { try { action(); } catch (Exception ex) { Log(ex); throw; } } 我不

在同步世界中,我有一个TryExecute函数来包装try/catch/log逻辑以供重用,如下所示:

TryExecute(() => SyncFunction());

private static void TryExecute(Action action)
{
    try
    {
        action();
    }
    catch (Exception ex)
    {
        Log(ex);
        throw;
    }
}
我不明白如何将其重写为异步/等待模式

据我所知,我有五种有效的方法将其重写为async/await(忽略任何其他VisualStudio警告)

将原始同步
TryExecute()
与异步委托一起使用:

(1) TryExecute(async () => await AsyncFunction());
(2) await TryExecuteTask(() => AsyncFunction());
(3) await TryExecuteTask(async () => await AsyncFunction());

private static Task TryExecuteTask(Func<Task> asyncAction)
{
    try
    {
        return asyncAction();
    }
    catch (Exception ex)
    {
        Log(ex);
        throw;
    }
}
(4) await TryExecuteAsync(() => AsyncFunction());
(5) await TryExecuteAsync(async () => await AsyncFunction());

private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
    try
    {
        await asyncAction();
    }
    catch (Exception ex)
    {
        Log(ex);
        throw;
    }
}
它似乎不再等待了,
TryExecute()
在不等待
AsyncFunction()
完成的情况下通过

重写到新的sync
TryExecuteTask()
返回任务,使用或不使用异步委托调用该任务:

(1) TryExecute(async () => await AsyncFunction());
(2) await TryExecuteTask(() => AsyncFunction());
(3) await TryExecuteTask(async () => await AsyncFunction());

private static Task TryExecuteTask(Func<Task> asyncAction)
{
    try
    {
        return asyncAction();
    }
    catch (Exception ex)
    {
        Log(ex);
        throw;
    }
}
(4) await TryExecuteAsync(() => AsyncFunction());
(5) await TryExecuteAsync(async () => await AsyncFunction());

private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
    try
    {
        await asyncAction();
    }
    catch (Exception ex)
    {
        Log(ex);
        throw;
    }
}
但是如果我从
AsyncFunction()
内部抛出
Exception
,那么上述五种方法都无法捕获
异常。所有已停止,出现未处理的异常。只有不带委托的catch工作:

(0) try
    {
        await AsyncFunction();
    }
    catch (Exception ex)
    {
        Log(ex);
    }
这意味着我不能使用从(1)到(5)的任何形式的
TryExecute()
来重用try/catch/log逻辑,我只能像(0)一样到处重复try/catch/log

我的整个控制台代码如下:

class Program
{
    async static Task Main(string[] args)
    {
        // Original sync way
        TryExecute(() => SyncFunction());

        Console.WriteLine("0");
        try
        {
            await AsyncFunction();
        }
        catch (Exception ex)
        {
            Log(ex);
        }

        ////Console.WriteLine("1");
        ////TryExecute(async () => await AsyncFunction());

        ////Console.WriteLine("2");
        ////await TryExecuteTask(() => AsyncFunction());
        ////Console.WriteLine("3");
        ////await TryExecuteTask(async () => await AsyncFunction());

        ////Console.WriteLine("4");
        ////await TryExecuteAsync(() => AsyncFunction());
        ////Console.WriteLine("5");
        ////await TryExecuteAsync(async () => await AsyncFunction());

        Console.WriteLine("Finished without unhandled exception.");
    }

    private static void SyncFunction()
    {
        Console.WriteLine("SyncFunction starting");
        Thread.Sleep(500);
        Console.WriteLine("SyncFunction starting");
        throw new Exception();
    }

    private async static Task AsyncFunction()
    {
        Console.WriteLine("AsyncFunction starting");
        await Task.Run(() =>
        {

            Console.WriteLine("Sleep starting");
            Thread.Sleep(500);
            Console.WriteLine("Sleep end");
            throw new Exception();
        });
        Console.WriteLine("AsyncFunction end");
    }

    private static void TryExecute(Action action)
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            Log(ex);
        }
    }

    private static Task TryExecuteTask(Func<Task> asyncAction)
    {
        try
        {
            return asyncAction();
        }
        catch (Exception ex)
        {
            Log(ex);
            throw;
        }
    }

    private async static Task TryExecuteAsync(Func<Task> asyncAction)
    {
        try
        {
            await asyncAction();
        }
        catch (Exception ex)
        {
            Log(ex);
            throw;
        }
    }

    private static void Log(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}
类程序
{
异步静态任务主(字符串[]args)
{
//原始同步方式
TryExecute(()=>SyncFunction());
控制台写入线(“0”);
尝试
{
等待异步函数();
}
捕获(例外情况除外)
{
对数(ex);
}
////控制台。写入线(“1”);
////TryExecute(异步()=>等待异步函数());
////控制台。写入线(“2”);
////等待TryExecuteTask(()=>AsyncFunction());
////控制台。写入线(“3”);
////wait-TryExecuteTask(async()=>wait-AsyncFunction());
////控制台。写入线(“4”);
////等待TryExecuteAsync(()=>AsyncFunction());
////控制台。写入线(“5”);
////await-TryExecuteAsync(async()=>await-AsyncFunction());
WriteLine(“完成时没有未处理的异常”);
}
私有静态void SyncFunction()
{
Console.WriteLine(“同步功能启动”);
睡眠(500);
Console.WriteLine(“同步功能启动”);
抛出新异常();
}
专用异步静态任务AsyncFunction()
{
Console.WriteLine(“异步函数启动”);
等待任务。运行(()=>
{
控制台写入线(“睡眠启动”);
睡眠(500);
控制台写入线(“睡眠结束”);
抛出新异常();
});
Console.WriteLine(“异步函数结束”);
}
私有静态无效TryExecute(操作)
{
尝试
{
动作();
}
捕获(例外情况除外)
{
对数(ex);
}
}
专用静态任务TryExecuteTask(Func asyncAction)
{
尝试
{
返回asyncAction();
}
捕获(例外情况除外)
{
对数(ex);
投掷;
}
}
专用异步静态任务TryExecuteAsync(Func asyncAction)
{
尝试
{
等待异步操作();
}
捕获(例外情况除外)
{
对数(ex);
投掷;
}
}
私有静态无效日志(异常ex)
{
控制台写入线(例如消息);
}
}

由于未处理的异常,我只能在
Main()

class Program
{
    async static Task Main(string[] args)
    {
        await TryExecuteAsync(AsyncFunction);
        Console.WriteLine("Finished without unhandled exception.");
    }

    private async static Task AsyncFunction()
    {
        Console.WriteLine("AsyncFunction starting");
        await Task.Run(() =>
        {

            Console.WriteLine("Sleep starting");
            Thread.Sleep(3000);
            Console.WriteLine("Sleep end");
            throw new Exception();
        });
        Console.WriteLine("AsyncFunction end");
    }

    private async static Task TryExecuteAsync(Func<Task> asyncAction)
    {
        try
        {
            await asyncAction();
        }
        catch (Exception ex)
        {
            Log(ex);
            throw;
        }
    }

    private static void Log(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}
我不明白如何将其重写为异步/等待模式

转换为
async
时,第一步是转换方法调用的内容。在这种情况下,应该首先将委托转换为异步兼容委托

Action
是一个委托,它不接受任何参数,也没有返回值,如
void Method()
。不接受参数且没有返回值的异步方法类似于
async Task method()
,因此

旁注:与学员打交道时,记住这一点尤为重要

一旦您将委托类型从
Action
更改为
Func
,您就可以
wait
其返回值,这将导致您的
TryExecute
方法更改为
async Task
,如下所示:

private static async Task TryExecuteAsync(Func<Task> asyncAction)
{
  try
  {
    await asyncAction();
  }
  catch (Exception ex)
  {
    Log(ex);
    throw;
  }
}
private静态异步任务TryExecuteAsync(Func asyncAction)
{
尝试
{
等待异步操作();
}
捕获(例外情况除外)
{
对数(ex);
投掷;
}
}
以上五种方法都不能捕获异常。所有已停止,出现未处理的异常

这实际上只是在调试器中运行代码的副作用。对于异步代码,有时确实会看到“未处理”的异常,而这些异常实际上并没有被处理。这是因为编译器生成的代码正在捕获异常并将其放在任务上,当您的代码
等待它时,它将被重新引发,然后您的代码将
捕获它。当原始异常被您的代码以外的东西捕获(它被编译器生成的代码捕获)时,调试器会有点惊慌失措,它无法知道这是完全正常的


因此,如果您只是继续通过调试器的“未处理”异常,您将看到它工作正常。

那么becoms(2)/(3)呢?但我还是不明白为什么(1)没有等待?我在委托中有
await
,没有任何警告,它运行(并在没有等待的情况下通过)。wait-TryExecuteAsync(()=>AsyncFunction())有什么问题?调用此方法时,您希望得到什么结果?如果要捕获在调用
TryExecuteAsync
中的
Log
后重新引发的异常,应在调用
TryExecuteAsync
前后放置一个
try/catch
子句。名为的函数通常不会引发异常。如果失败,则返回false。感谢所有答案/评论。我疯了