如何重载函数以接受异步和同步版本的回调参数

man*_*nok 3 c# overloading task multitasking async-await

public static T SyncVer<T>(Func<T> callback)
{
    using (new LogContext("new logging context"))
    {
        try
        {
            return callback();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);

            throw;
        }
    }
}

public static async Task<T> AsyncVer<T>(Func<Task<T>> callback)
{
    using (new LogContext("new logging context"))
    {
        try
        {
            return await callback();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);

            throw;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请考虑上面的代码。您可能会看到两个函数中的大部分代码是相同的。我正在寻找一种方法,通过将它们重载为一个来将它们分组,这样我就不需要复制内容。有没有办法去掉两个函数中相似的部分?

提供的任何帮助将不胜感激。提前致谢。

AAA*_*ddd 5

注意Task.FromResult会分配,并且GetAwaiter().GetResult()可能死锁

\n

就我个人而言,我只会创建两个方法而不用担心它。

\n

然而,另一种(稍微更安全)的方法是将回调包装在ValueTask. ValueTasks如果它们同步执行,效果最好,但是它们有一些微妙的限制,并且永远不应等待多次。

\n

假设是,这都是关于创建可等待和不可等待的委托重载、代码重用,并且等待此调用的同步版本对您来说不是问题。

\n

给定

\n
public static async ValueTask<T> SomethingAsync<T>(Func<T> callback)\n =>  await SomethingAsync(() => new ValueTask<T>(callback()));\n\npublic static async ValueTask<T> SomethingAsync<T>(Func<Task<T>> callback)\n =>  await SomethingAsync(() => new ValueTask<T>(callback()));\n\npublic static async ValueTask<T> SomethingAsync<T>(Func<ValueTask<T>> callback)\n{\n   using (new LogContext("new logging context"))\n   {\n      try\n      {\n         return await callback();\n      }\n      catch (Exception ex)\n      {\n         Console.WriteLine(ex);\n         throw;\n      }\n   }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

用法

\n
public static string DoSomething()\n{\n   Console.WriteLine("execute sync");\n   return "sync result";\n}\n\npublic static async Task<string> DoSomethingAsync()\n{\n   Console.WriteLine("Execute async");\n   await Task.Delay(100);\n   return "async result";\n}\n\n\n...\n\nConsole.WriteLine(await SomethingAsync(DoSomething));\nConsole.WriteLine(await SomethingAsync(DoSomethingAsync));\n
Run Code Online (Sandbox Code Playgroud)\n

输出

\n
Create\nexecute sync\nDispose\nsync result\nCreate\nExecute async\nDispose\nasync result\n
Run Code Online (Sandbox Code Playgroud)\n

要提高效率,您可以省略包装器

\n

示例

\n
public static  ValueTask<T> SomethingAsync<T>(Func<T> callback)\n{\n   try\n   {\n      return SomethingAsync(() => new ValueTask<T>(callback()));\n   }\n   catch (Exception e)\n   {\n      return ValueTask.FromException<T>(e);\n   }\n}\n\npublic static ValueTask<T> SomethingAsync<T>(Func<Task<T>> callback)\n{\n   try\n   {\n      return SomethingAsync(() => new ValueTask<T>(callback()));\n   }\n   catch (Exception e)\n   {\n      return ValueTask.FromException<T>(e);\n   }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

注意ValueTask.FromException仅适用于.NET 5.0+

\n

这种方法的好处:

\n
    \n
  • 此方法的同步版本的分配较少。
  • \n
  • 不可能出现僵局
  • \n
  • 它不会阻止异步方法
  • \n
  • 它给你一个价值任务超载
  • \n
\n

缺点是

\n
    \n
  • 您需要将异步任务包装在ValueTask中,尽管它只是一个堆栈分配
  • \n
  • 您需要为每个方法创建两个重载(总共三个签名)
  • \n
  • 这两个版本都不能等待两次。
  • \n
  • 同步版本稍微慢一些,因为它创建了一个状态机
  • \n
\n

注意:我个人从来不需要这样做,我只会创建这两个方法\xc2\xaf\\_(\xe3\x83\x84)_/\xc2\xaf

\n
\n

其他资源

\n\n