简体   繁体   English

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

[英]How do I overload function to accept both async and synchronized version of callback parameter

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

Please consider the code above.请考虑上面的代码。 You may see most of the code in both functions are the same.您可能会看到两个函数中的大部分代码是相同的。 I am finding a way to group them up by overloading them into one (or if there a way to take out the similar part of both function) so that I don't need to duplicate the content?我正在寻找一种通过将它们重载为一个来将它们分组的方法(或者如果有办法取出两个函数的相似部分),这样我就不需要复制内容了?

Any help provided will be appreciated.提供的任何帮助将不胜感激。 Thanks in advance.提前致谢。

I would try something like this:我会尝试这样的事情:

public static T SyncVer<T>(Func<T> callback)
{
    return AsyncVer(() => Task.FromResult(callback())).GetAwaiter().GetResult();
}

Note that Task.FromResult will allocate, and GetAwaiter().GetResult() may dead lock注意Task.FromResult会分配, GetAwaiter().GetResult()可能会死锁

Personally I would just create two methods and not worry about it.就我个人而言,我只会创建两种方法而不用担心它。

However, another (and slightly safer) approach is to wrap your callbacks in a ValueTask .但是,另一种(并且稍微安全一点)的方法是将回调包装在ValueTask中。 ValueTasks work best if they execute synchronously, however they have a few subtle limitations and should never be awaited more than once.如果ValueTasks同步执行,它们的工作效果最好,但是它们有一些微妙的限制,并且永远不应等待超过一次。

The assumption is, this is all about creation an awaitable and non awaitable delegate overload, code reuse, and awaiting the sync version of this call is not a problem for you.假设是,这一切都是关于创建一个可等待和不可等待的委托重载、代码重用,并且等待此调用的同步版本对您来说不是问题。

Given给定

public static async ValueTask<T> SomethingAsync<T>(Func<T> callback)
 =>  await SomethingAsync(() => new ValueTask<T>(callback()));

public static async ValueTask<T> SomethingAsync<T>(Func<Task<T>> callback)
 =>  await SomethingAsync(() => new ValueTask<T>(callback()));

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

Usage用法

public static string DoSomething()
{
   Console.WriteLine("execute sync");
   return "sync result";
}

public static async Task<string> DoSomethingAsync()
{
   Console.WriteLine("Execute async");
   await Task.Delay(100);
   return "async result";
}


...

Console.WriteLine(await SomethingAsync(DoSomething));
Console.WriteLine(await SomethingAsync(DoSomethingAsync));

Output Output

Create
execute sync
Dispose
sync result
Create
Execute async
Dispose
async result

To add some more efficiencies you can elide wrappers要增加一些效率,您可以省略包装器

Exmaple示例

public static  ValueTask<T> SomethingAsync<T>(Func<T> callback)
{
   try
   {
      return SomethingAsync(() => new ValueTask<T>(callback()));
   }
   catch (Exception e)
   {
      return ValueTask.FromException<T>(e);
   }
}

public static ValueTask<T> SomethingAsync<T>(Func<Task<T>> callback)
{
   try
   {
      return SomethingAsync(() => new ValueTask<T>(callback()));
   }
   catch (Exception e)
   {
      return ValueTask.FromException<T>(e);
   }
}

Note : ValueTask.FromException is only available for .NET 5.0+注意ValueTask.FromException仅适用于 .NET 5.0+

The benefits of this approach:这种方法的好处:

  • Less allocations for the sync version of this method.此方法的同步版本的分配较少。
  • There is no chance of a deadlock没有陷入僵局的可能
  • It doesn't block on async method它不会阻塞异步方法
  • It gives you a Value Task overload它给你一个价值任务重载

The downsides are缺点是

  • You need to wrap the async task in a ValueTask , though it's only a stack allocation您需要将异步任务包装在ValueTask中,尽管它只是一个堆栈分配
  • You will need to create two overloads per method (in total three signatures)您需要为每个方法创建两个重载(总共三个签名)
  • Neither version will can be awaited twice.这两个版本都不能等待两次。
  • The sync version is slightly slower as it creates a statemachine同步版本稍慢,因为它创建了一个状态机

Note: I personally have never had a need to do this, I would just create the two methods ¯\_(ツ)_/¯ .注意:我个人从来不需要这样做,我只会创建两个方法¯\_(ツ)_/¯


Additional resources其他资源

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM