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

請考慮上面的代碼。 您可能會看到兩個函數中的大部分代碼是相同的。 我正在尋找一種通過將它們重載為一個來將它們分組的方法(或者如果有辦法取出兩個函數的相似部分),這樣我就不需要復制內容了?

提供的任何幫助將不勝感激。 提前致謝。

我會嘗試這樣的事情:

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

注意Task.FromResult會分配, GetAwaiter().GetResult()可能會死鎖

就我個人而言,我只會創建兩種方法而不用擔心它。

但是,另一種(並且稍微安全一點)的方法是將回調包裝在ValueTask中。 如果ValueTasks同步執行,它們的工作效果最好,但是它們有一些微妙的限制,並且永遠不應等待超過一次。

假設是,這一切都是關於創建一個可等待和不可等待的委托重載、代碼重用,並且等待此調用的同步版本對您來說不是問題。

給定

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

用法

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

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

要增加一些效率,您可以省略包裝器

示例

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

注意ValueTask.FromException僅適用於 .NET 5.0+

這種方法的好處:

  • 此方法的同步版本的分配較少。
  • 沒有陷入僵局的可能
  • 它不會阻塞異步方法
  • 它給你一個價值任務重載

缺點是

  • 您需要將異步任務包裝在ValueTask中,盡管它只是一個堆棧分配
  • 您需要為每個方法創建兩個重載(總共三個簽名)
  • 這兩個版本都不能等待兩次。
  • 同步版本稍慢,因為它創建了一個狀態機

注意:我個人從來不需要這樣做,我只會創建兩個方法¯\_(ツ)_/¯


其他資源

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM