在 Emit 代碼中如何await一個異步方法

0. 前言

首先立馬解釋一波爲啥會有這樣一篇僞標題的Demo隨筆呢?
不是本人有知識誤區,或者要誤人子弟
由於你們都知道emit寫出來的都是同步方法,不可能await,至少如今這麼多年來沒有提供對應的功能
這是以前某天在微信羣看見討論怎麼emit一個異步方法幷包裝異步結構,簡單幾句文字也未能清晰的表達
因此趁着元旦節放假有點時間,
簡單列舉三種我知道方式去達到這樣的效果
三種方法都是繞過emit直接書寫emit代碼,而是將對應邏輯轉到其餘方法中,最後emit調用方法達到效果git

Demo 說明

原始方法是個延遲2秒以後返回55的方法:github

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.

優勢

仍是真正的異步async

缺點

成本比較大,畢竟這樣沒有了狀態機等等優化,(成本在 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.

優勢

執行時間上消耗很小優化

缺點

固然這樣 異步都變成了同步,因此可能會在某些狀況下咱們操做不當的代碼從而致使失去異步方法的優點ui

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代碼必須好好設計code

完整Demo放在

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

分享不易,若是能給予一點動力,不勝感激:關注一下本人的開源項目: Norns.Urd

相關文章
相關標籤/搜索