对于Linq关键字和await|对于Linq关键字和await,async异步关键字的扩展使用
最近在看neuecc大佬写的一些库:https://neuecc.medium.com/,其中对await,async以及linq一些关键字实现了自定义化使用,
使其不需要引用对应命名空间,不需要多线程就可以做一些自定义操作。因此进行学习,并在Unity3D下进行测试。
1.await,async关键字的自定义化扩展
只需要实现GetAwaiter公共方法即可,通过扩展方法实现也可以:
public static CoroutineAwaiterGetAwaiter(this WaitForSeconds instruction) { CoroutineAwaiter awaiter = new CoroutineAwaiter (instruction); return awaiter; }
该扩展方法可以实现Unity中的协程WaitForSeconds的异步封装。
这里看到会返回一个类型,实际上c#编译器关注返回的类型有没有实现INotifyCompletion接口
或ICriticalNotifyCompletion接口,这里以INotifyCompletion接口为例。
注意:此处代码参考Unity3dAsyncAwaitUtil(https://github.com/modesttree/Unity3dAsyncAwaitUtil)
对于返回类型,CoroutineAwaiter
public class CoroutineAwaiter: INotifyCompletion where T : YieldInstruction { private T mValue; private Action mOnCompleted; public bool IsCompleted => false; public CoroutineAwaiter(T value) { mValue = https://www.it610.com/article/value; }public T GetResult() => default; private IEnumerator CoroutineExec() { yield return mValue; mOnCompleted(); }#region INotifyCompletion void INotifyCompletion.OnCompleted(Action onCompleted) { mOnCompleted = onCompleted; CoroutineRunner.Instance.StartCoroutine(CoroutineExec()); } #endregion }
c#对该接口的调用流程,参考知乎(https://zhuanlan.zhihu.com/p/121792448):
- 先调用
t.GetAwaiter()
方法,取得等待器a
;- 调用
a.IsCompleted
取得布尔类型b
;- 如果
b=true
,则立即执行a.GetResult()
,取得运行结果;- 如果
b=false
,则看情况:
- 如果
a
没实现ICriticalNotifyCompletion
,则执行(a as INotifyCompletion).OnCompleted(action)
- 如果
a
实现了ICriticalNotifyCompletion
,则执行(a as ICriticalNotifyCompletion).OnCompleted(action)
- 执行随后暂停,
OnCompleted
完成后重新回到状态机;
对于该接口的实现,这里不考虑同步情况一律算作异步,所以通过CoroutineRunner开启一个协程序,
并在协程执行完成后调用mOnCompleted,通知c#的异步可以往下执行了。
此处代码经过测试,全部是回调函数实现的等待,并不会导致线程堵塞。
CoroutineRunner实现简单的全局协程托管,仅测试用:
文章图片
文章图片
using UnityEngine; public class CoroutineRunner : MonoBehaviour { private static CoroutineRunner sInstance; public static CoroutineRunner Instance => sInstance; private void Awake() { sInstance = this; } }
View Code
最终使用代码如下:
public class Test1 : MonoBehaviour { public void Start() { _ = WaitForSecondsExecTest(); //绕过警告提示 }async Task WaitForSecondsExecTest() { Debug.Log("Waiting 1 second..."); await new WaitForSeconds(1f); Debug.Log("Done!"); } }
2.Linq关键字的自定义化扩展 我们知道Linq可以写出类似Sql风格的关键字:
int[] arr = new[] {1, 2, 3}; var r = from item in arr where item > 0 select item;
unirx中对其进行了自定义化扩展:
// composing asynchronous sequence with LINQ query expressions var query = from google in ObservableWWW.Get("http://google.com/") from bing in ObservableWWW.Get("http://bing.com/") from unknown in ObservableWWW.Get(google + bing) select new { google, bing, unknown }; var cancel = query.Subscribe(x => Debug.Log(x)); // Call Dispose is cancel. cancel.Dispose();
研究了下它的代码,和GetAwaiter类似,只需要包含公共方法即可。
但是后来发现,类还必须包含一个泛型,C#编译器才可以成功识别:
public class Test : MonoBehaviour { public class Result//此处需有一个泛型才行 { public int Select (Func selector) { return 12; } }private void Start() { Result r = new Result (); var rInt = from item in r select new {item}; Debug.Log("rInt: " + rInt); //return 12. } }
对于where、skip等操作,应该也类似。
对于c#关键字自定义化的介绍就写到这里,至于怎么去用就仁者见仁智者见智了
这种写法最大的好处是不会引入System.Linq或是System.Threading等命名空间,
【对于Linq关键字和await|对于Linq关键字和await,async异步关键字的扩展使用】但如果要和多线程的异步混用或者用Task.WaitAll之类的操作,还是会引入很多多线程的东西。因此不建议混用。
推荐阅读
- 装聋作哑,关系融洽
- 社保代缴公司服务费包含哪些
- 越努力越幸福
- C语言的版本比较
- 关于响应式编程的十个问题
- 公司的盈利能力分析
- 精准!找到想要的关键字
- 你一笑,天就亮了。
- Java中有关Null的9件事
- 儿童名著整本阅读的重要性