【问题标题】:Async version of generic extensions通用扩展的异步版本
【发布时间】:2016-05-06 16:38:58
【问题描述】:

在 C#6 中,我有以下扩展:

public static void With<T>(this T value, Action<T> action) {
  action(value);
}

public static R With<T, R>(this T value, Func<T, R> function) {
  return function(value);
}

有没有办法获得这些扩展的异步版本?

更新

我正在添加一个示例来澄清。考虑(上下文是 EF 上下文):

IList<Post> posts = context.Posts.With(x => x.ToList());

如果我想使用 ToListAsync,现在该怎么做?

IList<Post> posts = await context.Posts.WithAsync(x => x.ToListAsync());

或者

IList<Post> posts = context.Posts.WithAsync(x => await x.ToListAsync());

最好的方法应该是什么?扩展会是什么样子?

【问题讨论】:

  • BeginInvoke 不会执行任何异步操作,而是会在主线程执行时阻塞...
  • public static async Task WithAsync&lt;T&gt;(this T value, Action&lt;T&gt; action)
  • @Gusman, new System.Action(System.Console.WriteLine).BeginInvoke(0, (cb) => { System.Console.WriteLine(cb.IsCompleted); }, null );
  • @Jay 在 .net 上谈论 Async 时是关于 async/await/Task 的,无论如何,从主线程调用 BeginInvoke 与将来某个时刻调用它完全相同,它在 main 上执行线程并将阻塞它,因此 BeginInvoke 不是异步的,异步应该并行执行并释放某种形式的线程资源,而 BeginInvoke 不会
  • 这些是您正在使用的确切功能还是实际功能不同(例如,它们是否有更多语句)?

标签: c#


【解决方案1】:

我强烈建议不要在你的扩展方法中使用async/await 来跳过状态机的生成。只需返回任务并在需要时等待或await他们

您也可以将第二种方法用于异步案例

public static R With<T>(this T value, Func<T, R> function) 
{
  return function(value);
}

或者你可以约束方法只用于异步

public static R WithAsync<T, R>(this T value, Func<T, R> function)
     where R : Task 
{
  return function(value);
}

【讨论】:

  • 请注意,根据作者的原始定义,您可以简单地选择R = TaskR = Task&lt;R'&gt;,因此这些专业化是不必要的。
【解决方案2】:

我在asynchronous delegate types 上有一篇博文。综上所述,Action&lt;T&gt;async 版本是Func&lt;T, Task&gt;Func&lt;T, R&gt;async 版本是Func&lt;T, Task&lt;R&gt;&gt;

我建议您提供所有重载以获得最大可用性:

public static void With<T>(this T value, Action<T> action) {
  action(value);
}

public static R With<T, R>(this T value, Func<T, R> function) {
  return function(value);
}

public static Task With<T>(this T value, Func<T, Task> function) {
  return function(value);
}

public static Task<R> With<T, R>(this T value, Func<T, Task<R>> function) {
  return function(value);
}

【讨论】:

  • 我应该用WithAsync调用最后两个方法吗?
  • 就个人而言,我希望他们被命名为WithAsync。但是,也有先例将它们保持相同的名称(例如,Task.Run)。
【解决方案3】:

就像使用任何其他功能一样:

public static async Task With<T>(this T value, Func<T, Task> action) {
  await action(value);
}

public static async Task<R> With<T, R>(this T value, Func<T, Task<R>> function) {
  return await function(value);
}

【讨论】:

  • 这不是在同步等待异步调用吗?
  • 您应该在Func&lt;T, Task&gt; 中添加一个CancellationToken 参数,使其成为Func&lt;T, CancellationToken, Task&gt;
  • 是的@casperOne,应该,但这只是演示如何将函数转换为异步,而不是如何实现函数的代码。
  • 无论您使用新任务,请参阅上面的答案或使用 BeginInvoke。
  • 请注意,根据作者的原始定义,您可以简单地选择R = TaskR = Task&lt;R'&gt;,因此这些专业化是不必要的。
【解决方案4】:
  • 让它async
  • 让它返回一个Task。如果您需要实际的返回类型,请使用 Task&lt;InsertReturnTypeHere&gt; 而不是 Task
  • 为了更好的衡量标准,将其命名为WithAsync。这将允许With&lt;T&gt; 与异步实现共存,这也是常见的约定。

public static async Task WithAsync<T>(this T value, Action<T> action) 
{
    await actionAsync(value);
}

【讨论】:

    【解决方案5】:
    public static void With<T>(this T value, Action<T> action) {
      action(value);
    }
    

    让您的操作自行安排任务。 With 不期望任何回报,因此它不必关心操作是如何运行的。

    public static R With<T, R>(this T value, Func<T, R> function) {
      return function(value);
    }
    

    提供一个返回任务的function。你可以像var y = await x.With(async z =&gt; { /* ... */ });一样使用它。

    结论:你不需要做任何改变。

    【讨论】:

      【解决方案6】:

      这取决于您打算进行的处理量以及您打算如何处理它。

      你需要一个线程吗?如果是这样,那么使用 Task 提供了一个很好的 Thread 替代方案。

      否则线程池中可能已经有相当多的线程可供您使用,See this question 您可以使用“BeginInvoke”访问这些线程。

      static void _TestLogicForBeginInvoke(int i)
          {
              System.Threading.Thread.Sleep(10);
      
              System.Console.WriteLine("Tested");
          }
      
          static void _Callback(IAsyncResult iar)
          {
              System.Threading.Thread.Sleep(10); 
      
              System.Console.WriteLine("Callback " + iar.CompletedSynchronously);
          }
      
          static void TestBeginInvoke()
          {
              //Callback is written after Tested and NotDone.
              var call = new System.Action<int>(_TestLogicForBeginInvoke);
      
              //Start the call
              var callInvocation = call.BeginInvoke(0, _Callback, null);
      
              //Write output
              System.Console.WriteLine("Output");
      
              int times = 0;
      
              //Wait for the call to be completed a few times
              while (false == callInvocation.IsCompleted && ++times < 10)
              {
                  System.Console.WriteLine("NotDone");
              }
      
              //Probably still not completed.
              System.Console.WriteLine("IsCompleted " + callInvocation.IsCompleted);
      
              //Can only be called once, should be called to free the thread assigned to calling the logic assoicated with BeginInvoke and the callback.
              call.EndInvoke(callInvocation);
          }//Callback 
      

      输出应该是:

      Output
      NotDone
      NotDone
      NotDone
      NotDone
      NotDone
      NotDone
      NotDone
      NotDone
      NotDone
      IsCompleted False
      Tested
      Callback False
      

      您定义的任何“委托”类型都可以使用委托实例的“BeginInvoke”方法在线程池上调用。另见MSDN

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-10-01
        • 2014-06-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多