using System; using System.Net; namespace Async15 { class Program { static void Main(string[] args) { using (var webClient = new WebClient { }) { webClient.Headers.Add("User-Agent", "AsyncContext 1.0"); var data = await webClient.DownloadStringTaskAsync("https://www.dntips.ir"); Console.WriteLine(data); } } } }
اضافه کردن واژهی کلیدی async به روالهای رخدادگردان void برنامههای دسکتاپ مجاز است؛ با توجه به اینکه متد async پیش از پایان کار به فراخوان بازگشت داده میشوند (ذات متدهای async به این نحو است). در برنامههای دسکتاپ، این بازگشت به UI event loop است؛ بنابراین برنامه بدون مشکل به کار خود ادامه خواهد داد. اما در اینجا، بازگشت متد Main، به معنای بازگشت به OS است و خاتمهی برنامه. به همین جهت کامپایلر از async کردن آن ممانعت میکند.
برای حل این مشکل در برنامههای کنسول و همچنین برنامههای سرویس ویندوز NT که دارای یک async-compatible context نیستند، میتوان از یک کتابخانهی کمکی سورس باز به نام Nito AsyncEx استفاده کرد. برای نصب آن دستور ذیل را در کنسول پاورشل نیوگت وارد کنید:
PM> Install-Package Nito.AsyncEx
using System; using System.Net; using Nito.AsyncEx; namespace Async15 { class Program { static void Main(string[] args) { AsyncContext.Run(async () => { using (var webClient = new WebClient()) { webClient.Headers.Add("User-Agent", "AsyncContext 1.0"); var data = await webClient.DownloadStringTaskAsync("https://www.dntips.ir"); Console.WriteLine(data); } }); } } }
async lambda
در مثال فوق از یک async lambda، برای فراخوانی استفاده شده است که به همراه دات نت 4.5 ارائه شدهاند:
Action, () => { } Func<Task>, async () => { await Task.Yield(); } Func<TResult>, () => { return 13; } Func<Task<TResult>>, async () => { await Task.Yield(); return 13; }
روش دوم استفاده از AsyncContext.Run و مقدار دهی Func of Task، تعریف یک متد مستقل async Task دار، به نحو ذیل است:
class Program { static async Task<int> AsyncMain() { .. } static int Main(string[] args) { return AsyncContext.Run(AsyncMain); } }
رخدادهای مرتبط با طول عمر برنامه را async تعریف نکنید
همانند متد Main که async تعریف کردن آن سبب بازگشت آنی روال کار به OS میشود و برنامه خاتمه مییابد، روالهای رخدادگردانی که با طول عمر یک برنامهی UI سر و کار دارند مانند Application_Launching، Application_Closing، Application_Activated و Application_Deactivated (خصوصا در برنامههای ویندوز 8) نیز نباید async void تعریف شوند (چون مطابق ذات متدهای async، بلافاصله به برنامه اعلام میکنند که کار تمام شد). در این موارد خاص نیز میتوان از متد AsyncContext.Run برای انجام اعمال async استفاده کرد.