You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

292 lines
12 KiB

  1. using Cysharp.Threading.Tasks.Internal;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Threading;
  5. namespace Cysharp.Threading.Tasks.Linq
  6. {
  7. public static partial class UniTaskAsyncEnumerable
  8. {
  9. public static IUniTaskAsyncEnumerable<TProperty> EveryValueChanged<TTarget, TProperty>(TTarget target, Func<TTarget, TProperty> propertySelector, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<TProperty> equalityComparer = null, bool cancelImmediately = false)
  10. where TTarget : class
  11. {
  12. var unityObject = target as UnityEngine.Object;
  13. var isUnityObject = target is UnityEngine.Object; // don't use (unityObject == null)
  14. if (isUnityObject)
  15. {
  16. return new EveryValueChangedUnityObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming, cancelImmediately);
  17. }
  18. else
  19. {
  20. return new EveryValueChangedStandardObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming, cancelImmediately);
  21. }
  22. }
  23. }
  24. internal sealed class EveryValueChangedUnityObject<TTarget, TProperty> : IUniTaskAsyncEnumerable<TProperty>
  25. {
  26. readonly TTarget target;
  27. readonly Func<TTarget, TProperty> propertySelector;
  28. readonly IEqualityComparer<TProperty> equalityComparer;
  29. readonly PlayerLoopTiming monitorTiming;
  30. readonly bool cancelImmediately;
  31. public EveryValueChangedUnityObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, bool cancelImmediately)
  32. {
  33. this.target = target;
  34. this.propertySelector = propertySelector;
  35. this.equalityComparer = equalityComparer;
  36. this.monitorTiming = monitorTiming;
  37. this.cancelImmediately = cancelImmediately;
  38. }
  39. public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
  40. {
  41. return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken, cancelImmediately);
  42. }
  43. sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
  44. {
  45. readonly TTarget target;
  46. readonly UnityEngine.Object targetAsUnityObject;
  47. readonly IEqualityComparer<TProperty> equalityComparer;
  48. readonly Func<TTarget, TProperty> propertySelector;
  49. readonly CancellationToken cancellationToken;
  50. readonly CancellationTokenRegistration cancellationTokenRegistration;
  51. bool first;
  52. TProperty currentValue;
  53. bool disposed;
  54. public _EveryValueChanged(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken, bool cancelImmediately)
  55. {
  56. this.target = target;
  57. this.targetAsUnityObject = target as UnityEngine.Object;
  58. this.propertySelector = propertySelector;
  59. this.equalityComparer = equalityComparer;
  60. this.cancellationToken = cancellationToken;
  61. this.first = true;
  62. if (cancelImmediately && cancellationToken.CanBeCanceled)
  63. {
  64. cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  65. {
  66. var source = (_EveryValueChanged)state;
  67. source.completionSource.TrySetCanceled(source.cancellationToken);
  68. }, this);
  69. }
  70. TaskTracker.TrackActiveTask(this, 2);
  71. PlayerLoopHelper.AddAction(monitorTiming, this);
  72. }
  73. public TProperty Current => currentValue;
  74. public UniTask<bool> MoveNextAsync()
  75. {
  76. if (disposed) return CompletedTasks.False;
  77. completionSource.Reset();
  78. if (cancellationToken.IsCancellationRequested)
  79. {
  80. completionSource.TrySetCanceled(cancellationToken);
  81. return new UniTask<bool>(this, completionSource.Version);
  82. }
  83. if (first)
  84. {
  85. first = false;
  86. if (targetAsUnityObject == null)
  87. {
  88. return CompletedTasks.False;
  89. }
  90. this.currentValue = propertySelector(target);
  91. return CompletedTasks.True;
  92. }
  93. return new UniTask<bool>(this, completionSource.Version);
  94. }
  95. public UniTask DisposeAsync()
  96. {
  97. if (!disposed)
  98. {
  99. cancellationTokenRegistration.Dispose();
  100. disposed = true;
  101. TaskTracker.RemoveTracking(this);
  102. }
  103. return default;
  104. }
  105. public bool MoveNext()
  106. {
  107. if (disposed || targetAsUnityObject == null)
  108. {
  109. completionSource.TrySetResult(false);
  110. DisposeAsync().Forget();
  111. return false;
  112. }
  113. if (cancellationToken.IsCancellationRequested)
  114. {
  115. completionSource.TrySetCanceled(cancellationToken);
  116. return false;
  117. }
  118. TProperty nextValue = default(TProperty);
  119. try
  120. {
  121. nextValue = propertySelector(target);
  122. if (equalityComparer.Equals(currentValue, nextValue))
  123. {
  124. return true;
  125. }
  126. }
  127. catch (Exception ex)
  128. {
  129. completionSource.TrySetException(ex);
  130. DisposeAsync().Forget();
  131. return false;
  132. }
  133. currentValue = nextValue;
  134. completionSource.TrySetResult(true);
  135. return true;
  136. }
  137. }
  138. }
  139. internal sealed class EveryValueChangedStandardObject<TTarget, TProperty> : IUniTaskAsyncEnumerable<TProperty>
  140. where TTarget : class
  141. {
  142. readonly WeakReference<TTarget> target;
  143. readonly Func<TTarget, TProperty> propertySelector;
  144. readonly IEqualityComparer<TProperty> equalityComparer;
  145. readonly PlayerLoopTiming monitorTiming;
  146. readonly bool cancelImmediately;
  147. public EveryValueChangedStandardObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, bool cancelImmediately)
  148. {
  149. this.target = new WeakReference<TTarget>(target, false);
  150. this.propertySelector = propertySelector;
  151. this.equalityComparer = equalityComparer;
  152. this.monitorTiming = monitorTiming;
  153. this.cancelImmediately = cancelImmediately;
  154. }
  155. public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
  156. {
  157. return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken, cancelImmediately);
  158. }
  159. sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
  160. {
  161. readonly WeakReference<TTarget> target;
  162. readonly IEqualityComparer<TProperty> equalityComparer;
  163. readonly Func<TTarget, TProperty> propertySelector;
  164. readonly CancellationToken cancellationToken;
  165. readonly CancellationTokenRegistration cancellationTokenRegistration;
  166. bool first;
  167. TProperty currentValue;
  168. bool disposed;
  169. public _EveryValueChanged(WeakReference<TTarget> target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken, bool cancelImmediately)
  170. {
  171. this.target = target;
  172. this.propertySelector = propertySelector;
  173. this.equalityComparer = equalityComparer;
  174. this.cancellationToken = cancellationToken;
  175. this.first = true;
  176. if (cancelImmediately && cancellationToken.CanBeCanceled)
  177. {
  178. cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  179. {
  180. var source = (_EveryValueChanged)state;
  181. source.completionSource.TrySetCanceled(source.cancellationToken);
  182. }, this);
  183. }
  184. TaskTracker.TrackActiveTask(this, 2);
  185. PlayerLoopHelper.AddAction(monitorTiming, this);
  186. }
  187. public TProperty Current => currentValue;
  188. public UniTask<bool> MoveNextAsync()
  189. {
  190. if (disposed) return CompletedTasks.False;
  191. completionSource.Reset();
  192. if (cancellationToken.IsCancellationRequested)
  193. {
  194. completionSource.TrySetCanceled(cancellationToken);
  195. return new UniTask<bool>(this, completionSource.Version);
  196. }
  197. if (first)
  198. {
  199. first = false;
  200. if (!target.TryGetTarget(out var t))
  201. {
  202. return CompletedTasks.False;
  203. }
  204. this.currentValue = propertySelector(t);
  205. return CompletedTasks.True;
  206. }
  207. return new UniTask<bool>(this, completionSource.Version);
  208. }
  209. public UniTask DisposeAsync()
  210. {
  211. if (!disposed)
  212. {
  213. cancellationTokenRegistration.Dispose();
  214. disposed = true;
  215. TaskTracker.RemoveTracking(this);
  216. }
  217. return default;
  218. }
  219. public bool MoveNext()
  220. {
  221. if (disposed || !target.TryGetTarget(out var t))
  222. {
  223. completionSource.TrySetResult(false);
  224. DisposeAsync().Forget();
  225. return false;
  226. }
  227. if (cancellationToken.IsCancellationRequested)
  228. {
  229. completionSource.TrySetCanceled(cancellationToken);
  230. return false;
  231. }
  232. TProperty nextValue = default(TProperty);
  233. try
  234. {
  235. nextValue = propertySelector(t);
  236. if (equalityComparer.Equals(currentValue, nextValue))
  237. {
  238. return true;
  239. }
  240. }
  241. catch (Exception ex)
  242. {
  243. completionSource.TrySetException(ex);
  244. DisposeAsync().Forget();
  245. return false;
  246. }
  247. currentValue = nextValue;
  248. completionSource.TrySetResult(true);
  249. return true;
  250. }
  251. }
  252. }
  253. }