c# 在Emit代码中如何await一个异步方法

0. 前言

首先立马解释一波为啥会有这样一篇伪标题的Demo随笔呢?
不是本人有知识误区,或者要误人子弟
因为大家都知道emit写出来的都是同步方法,不可能await,至少现在这么多年来没有提供对应的功能
这是之前某天在微信群看见讨论怎么emit一个异步方法并包装异步结构,简单几句文字也未能清晰的表达
所以趁着元旦节放假有点时间,
简单列举三种我知道方式去达到这样的效果
三种方法都是绕过emit直接书写emit代码,而是将对应逻辑转到其他方法中,最后emit调用方法达到效果

Demo 说明

原始方法是个延迟2秒之后返回55的方法:

  public static async Task<int> GetV()
  {
   await Task.Delay(2000);
   return 55;
  }

现在我们需要把 55 的结果加 6 ,让最终的结果变为 61

我们的测试方法是这样,会输出一些简单的时间,帮助我们了解执行顺序和异步情况

  private static async Task Test(MethodInfo method, MethodInfo awaitMehtod)
  {
   var caller = CreateCaller(method, awaitMehtod);
   Console.WriteLine($"Start {awaitMehtod.Name} at: {DateTime.Now}.");
   var task = caller();
   Console.WriteLine($"Call done at: {DateTime.Now}.");
   var number = await task;
   Console.WriteLine($"Hello {number} at: {DateTime.Now}.");
   Console.WriteLine($"End at: {DateTime.Now}.");
   Console.WriteLine();
  }

1. ContinueWith

  public static Func<Task<int>> CreateCaller(MethodInfo method, MethodInfo awaitMehtod)
  {
   var m = new DynamicMethod(Guid.NewGuid().ToString("N"), typeof(Task<int>), Type.EmptyTypes);
   var il = m.GetILGenerator();
   il.Emit(OpCodes.Call, method);
   il.Emit(OpCodes.Call, typeof(Program).GetMethod(nameof(Program.AddSixUseContinueWith))); // 这里是差异点
   il.Emit(OpCodes.Ret);

   return m.CreateDelegate(typeof(Func<Task<int>>)) as Func<Task<int>>;
  }

  public static Task<int> AddSixUseContinueWith(Task<int> task)
  {
   return task.ContinueWith(i =>
   {
    Console.WriteLine($"AddSixUseContinueWith is: {DateTime.Now}.");
    return i.Result + 6;
   });
  }

测试结果:

Start AddSixUseContinueWith at: 2021/1/2 13:34:55.
Call done at: 2021/1/2 13:34:55.
AddSixUseContinueWith is: 2021/1/2 13:34:57.
Hello 61 at: 2021/1/2 13:34:57.
End at: 2021/1/2 13:34:57.

优点
还是真正的异步

缺点
成本比较大,毕竟这样没有了状态机等等优化,(成本在 ns 级别哦,不是大家想的 ms哦)

2. GetAwaiter().GetResult()

  public static Func<Task<int>> CreateCaller(MethodInfo method, MethodInfo awaitMehtod)
  {
   var m = new DynamicMethod(Guid.NewGuid().ToString("N"), typeof(Task<int>), Type.EmptyTypes);
   var il = m.GetILGenerator();
   il.Emit(OpCodes.Call, method);
   il.Emit(OpCodes.Call, typeof(Program).GetMethod(nameof(Program.AddSixUseAwaiter))); // 这里是差异点
   il.Emit(OpCodes.Ret);

   return m.CreateDelegate(typeof(Func<Task<int>>)) as Func<Task<int>>;
  }

  public static Task<int> AddSixUseAwaiter(Task<int> task)
  {
   var r = task.ConfigureAwait(false).GetAwaiter().GetResult() + 6;
   Console.WriteLine($"AddSixUseAwaiter is: {DateTime.Now}.");
   return Task.FromResult(r);
  }

测试结果:

Start AddSixUseAwaiter at: 2021/1/2 13:34:57.
AddSixUseAwaiter is: 2021/1/2 13:34:59.
Call done at: 2021/1/2 13:34:59.
Hello 61 at: 2021/1/2 13:34:59.
End at: 2021/1/2 13:34:59.

优点
执行时间上消耗很小

缺点
当然这样 异步都变成了同步,所以可能会在某些情况下我们操作不当的代码从而导致失去异步方法的优势

3. async/await

  public static Func<Task<int>> CreateCaller(MethodInfo method, MethodInfo awaitMehtod)
  {
   var m = new DynamicMethod(Guid.NewGuid().ToString("N"), typeof(Task<int>), Type.EmptyTypes);
   var il = m.GetILGenerator();
   il.Emit(OpCodes.Call, method);
   il.Emit(OpCodes.Call, typeof(Program).GetMethod(nameof(Program.AddSixUseAsyncAwait))); // 这里是差异点
   il.Emit(OpCodes.Ret);

   return m.CreateDelegate(typeof(Func<Task<int>>)) as Func<Task<int>>;
  }

  public static async Task<int> AddSixUseAsyncAwait(Task<int> task)
  {
   var r = await task;
   Console.WriteLine($"AddSixUseAsyncAwait is: {DateTime.Now}.");
   return r + 6;
  }

测试结果:

Start AddSixUseAsyncAwait at: 2021/1/2 13:34:59.
Call done at: 2021/1/2 13:34:59.
AddSixUseAsyncAwait is: 2021/1/2 13:35:01.
Hello 61 at: 2021/1/2 13:35:01.
End at: 2021/1/2 13:35:01.

优点
async / await 本身的优势都没有损失

缺点
原本想在 emit 中 对result的处理逻辑 必须迁移到 async / await 方法中,emit代码必须好好设计

完整Demo放在

https://github.com/fs7744/grocery/blob/main/csharp/emit_await/EmitAwaitDemo/Program.cs

分享不易,如果能给予一点动力,不胜感激:关注一下本人的开源项目: Norns.Urd

以上就是c# 在Emit代码中如何await一个异步方法的详细内容,更多关于c# Emit代码await一个异步方法的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解C#中 Thread,Task,Async/Await,IAsyncResult的那些事儿

    说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部分可以同时执行:对于比较耗时的操作(例如io,数据库操作),或者等待响应(如WCF通信)的操作,可以单独开启后台线程来执行,这样主线程就不会阻塞,可以继续往下执行:等到后台线程执行完毕,再通知主线程,然后做出对应操作! 在C#中开启新线程比较简单 static void Main(string[]

  • C#关键字async/await用法

    经过一番的探索,终于搞清楚关键字async/await 在.net4.5之后可用的巧妙之处,在这里记录一下也与大家分享一下个人的心得体会 async:异步执行 await:异步执行中的等待其执行完(最大的优点是它是后台等待,因此不阻塞GUI,界面交互非常的好) 使用async方法要定义async Task或者async Task<T> 最好不要定义async void方法来调用,async void是处理程序等,总结结论就是要使用async Task或者async Task<T>

  • .NET(C#):Emit创建异常处理的方法

    目录 Emit异常处理流程 显示Exception对象的Message属性 返回目录 Emit异常处理流程来看这种C#异常处理代码: 复制代码 代码如下: static void doo(Exception e) { try { throw e; } catch (ApplicationException ex) { Console.WriteLine("捕获ApplicationException"); } catch { Console.WriteLine("捕获Exce

  • 说说C#的async和await的具体用法

    C# 5.0中引入了async 和 await.这两个关键字可以让你更方便的写出异步代码. 看个例子: public class MyClass { public MyClass() { DisplayValue(); //这里不会阻塞 System.Diagnostics.Debug.WriteLine("MyClass() End."); } public Task<double> GetValueAsync(double num1, double num2) { re

  • 浅谈C# async await 死锁问题总结

    可能发生死锁的程序类型 1.WPF/WinForm程序 2.asp.net (不包括asp.net core)程序 死锁的产生原理 对异步方法返回的Task调用Wait()或访问Result属性时,可能会产生死锁. 下面的WPF代码会出现死锁: private void Button_Click_7(object sender, RoutedEventArgs e) { Method1().Wait(); } private async Task Method1() { await Task.D

  • 详解C#中的Async和Await用法

    这篇文章由Filip Ekberg为DNC杂志编写. 自跟随着.NET 4.5 及Visual Studio 2012的C# 5.0起,我们能够使用涉及到async和await关键字的新的异步模式.有很多不同观点认为,比起以前我们看到的,它的可读性和可用性是否更为突出.我们将通过一个例子来看下它跟现在的怎么不同. 线性代码vs非线性代码 大部分的软件工程师都习惯用一种线性的方式去编程,至少这是他们开始职业生涯时就被这样教导.当一个程序使用线性方式去编写,这意味着它的源代码读起来有的像Figure

  • 一文搞懂c# await,async执行流

    昨天有朋友在公众号发消息说看不懂await,async执行流,其实看不懂太正常了,因为你没经过社会的毒打,没吃过牢饭就不知道自由有多重要,没生过病就不知道健康有多重要,没用过ContinueWith就不知道await,async有多重要,下面我举两个案例佐证一下? 一:案例一 [嵌套下的异步] 写了这么多年的程序,相信大家都知道连接数据库少不了这几个对象,DbConnection,DbCommand,DbDataReader等等..先来看看ContinueWith在连接数据库时嵌套过深的尴尬.

  • 详解c# Emit技术

    我们常常有一个应用场景,由我们的C#代码,动态生成一个EXE,其应用场景可以非常多,比如软件授权,可以输入授权信息后,生成一个授权的DLL等,那如何实现这个功能呢,就要提到一个技术Emit. 1.Emit概述 Emit,可以称为发出或者产生.在Framework中,与Emit相关的类基本都存在于System.Reflection.Emit命名 空间下.可见Emit是作为反射的一个元素存在的.说道反射,大家应该都不陌生,它允许我们查看程序集的元素据,从而取得形如程序集包含哪些类型,类型包 含哪些方

  • 浅谈C#中的Async和Await的用法详解

    众所周知C#提供Async和Await关键字来实现异步编程.在本文中,我们将共同探讨并介绍什么是Async 和 Await,以及如何在C#中使用Async 和 Await. 同样本文的内容也大多是翻译的,只不过加上了自己的理解进行了相关知识点的补充,如果你认为自己的英文水平还不错,大可直接跳转到文章末尾查看原文链接进行阅读. 写在前面 自从C# 5.0时代引入async和await关键字后,异步编程就变得流行起来.尤其在现在的.NET Core时代,如果你的代码中没有出现async或者await

  • 聊一聊C# 8.0中的await foreach使用

    AsyncStreamsInCShaper8.0 很开心今天能与大家一起聊聊C# 8.0中的新特性-Async Streams,一般人通常看到这个词表情是这样. 简单说,其实就是C# 8.0中支持await foreach. 或者说,C# 8.0中支持异步返回枚举类型async Task<IEnumerable<T>>. 好吧,还不懂?Good,这篇文章就是为你写的,看完这篇文章,你就能明白它的神奇之处了. 为什么写这篇文章 Async Streams这个功能已经发布很久了,在去年

随机推荐