论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
只需一步,快速开始
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
IT评测·应用市场-qidao123.com
»
论坛
›
数据库
›
Oracle
›
C# 实现你自己的异步方法
C# 实现你自己的异步方法
八卦阵
金牌会员
|
2023-3-3 01:17:54
|
显示全部楼层
|
阅读模式
楼主
主题
971
|
帖子
971
|
积分
2913
背景
最近在重构自己曾经的代码, 具体需求是在Unity等待如一个模型动画, 一段ui动画 如下:
Await的目标
await的目标是一个可等待对象, 而拥有GetAwaiter方法并且该方法拥有合适返回值的目标即可称为可等待对象(暂时你还不需要知道返回值需要符合什么规则,待会儿Studio会告诉你)
首先,我们通过一些简单的代码来模拟Unity的Slider, 并且实现一个方法来模拟ui动画, 然后在实例化它来看看效果
public class Slider
{
public void ValueTo(int value)
{
Task.Run(async () =>
{
Console.Write("HP: ");
for (int i = 0; i < value; i++)
{
await Task.Delay(350);
Console.Write("■");
}
Console.Write("\n");
});
}
}
复制代码
效果不错, 现在你希望可以await slider.ValueTo 方法, 而不是再通过使用回调函数传来传去来监听如动画是否结束
首先我们知道await的目标是一个拥有GetAwaiter方法的对象
简单点, 我们令Slider本身是可等待的, 也即我们需要在Slider上实现一个GetAwaiter方法, 已知且其返回值有一定的限制, 我们假设其返回值为SliderAwaiter
public class SliderAwaiter
{
}
public class Slider
{
public SliderAwaiter GetAwaiter()
{
SliderAwaiter awaiter = new SliderAwaiter();
return awaiter;
}
public Slider ValueToAsync(int value)
{
/*
* Task是可等待的, 这里直接返回Task即可在这里达成我们的目标
* 但本文的主要讨论的是实现 自己 的异步方法
* 并且UI动画你可以以此法逃课, 但播放动画就不好逃了
*/
Task.Run(async () =>
{
Console.Write("HP: ");
for (int i = 0; i < value; i++)
{
await Task.Delay(350);
Console.Write("■");
}
Console.Write("\n");
});
return this;
}
}
复制代码
当我们尝试在Main中直接await ValueToAsync方法时, 编译器报错(“Program.SliderAwaiter”未包含“IsCompleted”的定义), 我们进行修补。又报错(“Program.SliderAwaiter”不实现“INotifyCompletion”), 继续修, 最后一个报错(“Program.SliderAwaiter”未包含“GetResult”的定义), 补上
经过修补, 我们得到这样的SliderAwaiter
public class SliderAwaiter : INotifyCompletion
{
public bool IsCompleted => false;
public void OnCompleted(Action continuation)
{
}
public void GetResult() { }
}
复制代码
直接await ValueToAsync测试,发现Slider表现正常, 但await并没有触发
这里暂时先直接告诉你答案, 你需要手动触发OnCompleted传入的continuation来触发await后的代码
简单改造一下SliderAwaiter和Slider得到下面的代码
public class SliderAwaiter : INotifyCompletion
{
public bool IsCompleted => false;
public Action onFinish;
public void OnCompleted(Action continuation)
{
if (IsCompleted)
{
continuation?.Invoke();
onFinish?.Invoke();
}
else
{
onFinish += continuation;
}
}
/// <summary>
/// 手动触发 continuation
/// </summary>
public void ReportResult()
{
onFinish?.Invoke();
}
public void GetResult() { }
}
public class Slider
{
SliderAwaiter awaiter;
public SliderAwaiter GetAwaiter()
{
SliderAwaiter awaiter = new SliderAwaiter();
this.awaiter = awaiter;
return awaiter;
}
public Slider ValueToAsync(int value)
{
Task.Run(async () =>
{
Console.Write("HP: ");
for (int i = 0; i < value; i++)
{
await Task.Delay(350);
Console.Write("■");
}
Console.Write("\n");
awaiter?.ReportResult();
});
return this;
}
}
复制代码
在Main函数中测试一下, 神奇的事情发生了, await后的代码成功且正确地执行了
你可能觉得有些奇妙, 你只是手动触发了OnCompleted传入的后续操作(类似Task.ContinueWith)就成功触发了结束等待, 你可能也会尝试在ReportResult中多次执行continuation, 但await的后续代码依旧只执行一次
OnCompleted中的continuation是什么, 它做了什么
了解他之前, 你可能需要先了解await是如何运行的, 首先你需要知道await的实现是基于状态机
简单点, 我们把测试代码改的简单点
static async Task Main(string[] args)
{
Console.WriteLine("before await");
await Task.Delay(500);
Console.WriteLine("after await");
Console.Read();
}
复制代码
然后通过dnSpy看看生成的中间语言是什么样的, 从下图和标注你应该可以清晰地看出程序的整体走向
通过简单测试代码的中间语言我们了解了await的大致走向, 那么回到最初的问题,continuation究竟做了什么
还原我们刚刚模拟Unity Slider的代码, 继续借助dnSpy, 二话不说找到ReportResult方法并且打上断点, 看看他做了什么
是的!一进来就发现一个惊喜, 第一步就跳转到一个叫做MoveNext的方法, 最终经过一番闪转腾挪我们又回到了的状态机里!
我们可以通过手动执行continuation来手动触发我们自己的awaiter!
至此, 你应该了解了如何实现自己的异步方法, 并且它不用依赖Task, TaskCompletionSource
最后附上完整的测试代码, 希望本片文章对你的异步之旅有所帮助
internal class Program
{
public class SliderAwaiter : INotifyCompletion
{
public bool IsCompleted => false;
public Action onFinish;
public void OnCompleted(Action continuation)
{
if (IsCompleted)
{
continuation?.Invoke();
onFinish?.Invoke();
}
else
{
onFinish += continuation;
}
}
/// <summary>
/// 手动触发 continuation
/// </summary>
public void ReportResult()
{
onFinish?.Invoke();
}
public void GetResult() { }
}
public class Slider
{
SliderAwaiter awaiter;
public SliderAwaiter GetAwaiter()
{
SliderAwaiter awaiter = new SliderAwaiter();
this.awaiter = awaiter;
return awaiter;
}
public Slider ValueToAsync(int value)
{
Task.Run(async () =>
{
Console.Write("HP: ");
for (int i = 0; i < value; i++)
{
await Task.Delay(350);
Console.Write("■");
}
Console.Write("\n");
awaiter?.ReportResult();
});
return this;
}
}
static async Task Main(string[] args)
{
Slider slider = new Slider();
Console.WriteLine("[ START ] PLAY UI_ANIMATION");
await slider.ValueToAsync(4);
Console.WriteLine("[ END ] PLAY UI_ANIMATION");
}
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
本帖子中包含更多资源
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
回复
使用道具
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
八卦阵
金牌会员
这个人很懒什么都没写!
楼主热帖
不可思议但又无处不在的漏洞,WEB安全 ...
【历史上的今天】6 月 2 日:苹果推出 ...
C#实现HTTP访问类HttpHelper
开源共建 | Dinky 扩展批流统一数据集 ...
企业应用超融合架构的设计实例及超融合 ...
袋鼠云春季生长大会圆满落幕,带来数实 ...
大规模 IoT 边缘容器集群管理的几种架 ...
重磅硬核 | 一文聊透对象在 JVM 中的内 ...
【软考】系统集成项目管理工程师(二) ...
ClickHouse 查询优化详细介绍
标签云
AI
运维
CIO
存储
服务器
快速回复
返回顶部
返回列表