c# 在Emit代碼中如何await一個(gè)異步方法
0. 前言
首先立馬解釋一波為啥會(huì)有這樣一篇偽標(biāo)題的Demo隨筆呢?
不是本人有知識誤區(qū),或者要誤人子弟
因?yàn)榇蠹叶贾纄mit寫出來的都是同步方法,不可能await,至少現(xiàn)在這么多年來沒有提供對應(yīng)的功能
這是之前某天在微信群看見討論怎么emit一個(gè)異步方法并包裝異步結(jié)構(gòu),簡單幾句文字也未能清晰的表達(dá)
所以趁著元旦節(jié)放假有點(diǎn)時(shí)間,
簡單列舉三種我知道方式去達(dá)到這樣的效果
三種方法都是繞過emit直接書寫emit代碼,而是將對應(yīng)邏輯轉(zhuǎn)到其他方法中,最后emit調(diào)用方法達(dá)到效果
Demo 說明
原始方法是個(gè)延遲2秒之后返回55的方法:
public static async Task<int> GetV() { await Task.Delay(2000); return 55; }
現(xiàn)在我們需要把 55 的結(jié)果加 6 ,讓最終的結(jié)果變?yōu)?61
我們的測試方法是這樣,會(huì)輸出一些簡單的時(shí)間,幫助我們了解執(zhí)行順序和異步情況
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))); // 這里是差異點(diǎn) 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; }); }
測試結(jié)果:
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.
優(yōu)點(diǎn)
還是真正的異步
缺點(diǎn)
成本比較大,畢竟這樣沒有了狀態(tài)機(jī)等等優(yōu)化,(成本在 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))); // 這里是差異點(diǎn) 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); }
測試結(jié)果:
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.
優(yōu)點(diǎn)
執(zhí)行時(shí)間上消耗很小
缺點(diǎn)
當(dāng)然這樣 異步都變成了同步,所以可能會(huì)在某些情況下我們操作不當(dāng)?shù)拇a從而導(dǎo)致失去異步方法的優(yōu)勢
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))); // 這里是差異點(diǎn) 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; }
測試結(jié)果:
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.
優(yōu)點(diǎn)
async / await 本身的優(yōu)勢都沒有損失
缺點(diǎn)
原本想在 emit 中 對result的處理邏輯 必須遷移到 async / await 方法中,emit代碼必須好好設(shè)計(jì)
完整Demo放在
https://github.com/fs7744/grocery/blob/main/csharp/emit_await/EmitAwaitDemo/Program.cs
分享不易,如果能給予一點(diǎn)動(dòng)力,不勝感激:關(guān)注一下本人的開源項(xiàng)目: Norns.Urd
以上就是c# 在Emit代碼中如何await一個(gè)異步方法的詳細(xì)內(nèi)容,更多關(guān)于c# Emit代碼await一個(gè)異步方法的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
c#圖片縮放圖片剪切功能實(shí)現(xiàn)(等比縮放)
c#圖片縮放剪切功能實(shí)現(xiàn),代碼中包含了c#圖片處理的一些基礎(chǔ)知識,與大家分享2013-12-12在Form_Load里面調(diào)用Focus無效的解決方法
在調(diào)用Form_Load的時(shí)候,F(xiàn)orm其實(shí)還沒有進(jìn)入展示階段,自然Focus()調(diào)用也就沒效果了。2013-02-02C# Winform實(shí)現(xiàn)圓角無鋸齒按鈕
這篇文章主要為大家詳細(xì)介紹了C# Winform實(shí)現(xiàn)圓角無鋸齒按鈕,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07C#實(shí)現(xiàn)簡單的點(diǎn)餐系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)簡單的點(diǎn)餐系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01winform c#中子窗體關(guān)閉刷新父窗體的實(shí)例
下面小編就為大家?guī)硪黄獁inform c#中子窗體關(guān)閉刷新父窗體的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02WinForm項(xiàng)目開發(fā)中Excel用法實(shí)例解析
這篇文章主要介紹了WinForm項(xiàng)目開發(fā)中Excel用法,非常實(shí)用,需要的朋友可以參考下2014-08-08