EveryValueChanged.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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)
  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);
  17. }
  18. else
  19. {
  20. return new EveryValueChangedStandardObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming);
  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. public EveryValueChangedUnityObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming)
  31. {
  32. this.target = target;
  33. this.propertySelector = propertySelector;
  34. this.equalityComparer = equalityComparer;
  35. this.monitorTiming = monitorTiming;
  36. }
  37. public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
  38. {
  39. return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken);
  40. }
  41. sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
  42. {
  43. readonly TTarget target;
  44. readonly UnityEngine.Object targetAsUnityObject;
  45. readonly IEqualityComparer<TProperty> equalityComparer;
  46. readonly Func<TTarget, TProperty> propertySelector;
  47. CancellationToken cancellationToken;
  48. bool first;
  49. TProperty currentValue;
  50. bool disposed;
  51. public _EveryValueChanged(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken)
  52. {
  53. this.target = target;
  54. this.targetAsUnityObject = target as UnityEngine.Object;
  55. this.propertySelector = propertySelector;
  56. this.equalityComparer = equalityComparer;
  57. this.cancellationToken = cancellationToken;
  58. this.first = true;
  59. TaskTracker.TrackActiveTask(this, 2);
  60. PlayerLoopHelper.AddAction(monitorTiming, this);
  61. }
  62. public TProperty Current => currentValue;
  63. public UniTask<bool> MoveNextAsync()
  64. {
  65. // return false instead of throw
  66. if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
  67. if (first)
  68. {
  69. first = false;
  70. if (targetAsUnityObject == null)
  71. {
  72. return CompletedTasks.False;
  73. }
  74. this.currentValue = propertySelector(target);
  75. return CompletedTasks.True;
  76. }
  77. completionSource.Reset();
  78. return new UniTask<bool>(this, completionSource.Version);
  79. }
  80. public UniTask DisposeAsync()
  81. {
  82. if (!disposed)
  83. {
  84. disposed = true;
  85. TaskTracker.RemoveTracking(this);
  86. }
  87. return default;
  88. }
  89. public bool MoveNext()
  90. {
  91. if (disposed || cancellationToken.IsCancellationRequested || targetAsUnityObject == null) // destroyed = cancel.
  92. {
  93. completionSource.TrySetResult(false);
  94. DisposeAsync().Forget();
  95. return false;
  96. }
  97. TProperty nextValue = default(TProperty);
  98. try
  99. {
  100. nextValue = propertySelector(target);
  101. if (equalityComparer.Equals(currentValue, nextValue))
  102. {
  103. return true;
  104. }
  105. }
  106. catch (Exception ex)
  107. {
  108. completionSource.TrySetException(ex);
  109. DisposeAsync().Forget();
  110. return false;
  111. }
  112. currentValue = nextValue;
  113. completionSource.TrySetResult(true);
  114. return true;
  115. }
  116. }
  117. }
  118. internal sealed class EveryValueChangedStandardObject<TTarget, TProperty> : IUniTaskAsyncEnumerable<TProperty>
  119. where TTarget : class
  120. {
  121. readonly WeakReference<TTarget> target;
  122. readonly Func<TTarget, TProperty> propertySelector;
  123. readonly IEqualityComparer<TProperty> equalityComparer;
  124. readonly PlayerLoopTiming monitorTiming;
  125. public EveryValueChangedStandardObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming)
  126. {
  127. this.target = new WeakReference<TTarget>(target, false);
  128. this.propertySelector = propertySelector;
  129. this.equalityComparer = equalityComparer;
  130. this.monitorTiming = monitorTiming;
  131. }
  132. public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
  133. {
  134. return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken);
  135. }
  136. sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
  137. {
  138. readonly WeakReference<TTarget> target;
  139. readonly IEqualityComparer<TProperty> equalityComparer;
  140. readonly Func<TTarget, TProperty> propertySelector;
  141. CancellationToken cancellationToken;
  142. bool first;
  143. TProperty currentValue;
  144. bool disposed;
  145. public _EveryValueChanged(WeakReference<TTarget> target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken)
  146. {
  147. this.target = target;
  148. this.propertySelector = propertySelector;
  149. this.equalityComparer = equalityComparer;
  150. this.cancellationToken = cancellationToken;
  151. this.first = true;
  152. TaskTracker.TrackActiveTask(this, 2);
  153. PlayerLoopHelper.AddAction(monitorTiming, this);
  154. }
  155. public TProperty Current => currentValue;
  156. public UniTask<bool> MoveNextAsync()
  157. {
  158. if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
  159. if (first)
  160. {
  161. first = false;
  162. if (!target.TryGetTarget(out var t))
  163. {
  164. return CompletedTasks.False;
  165. }
  166. this.currentValue = propertySelector(t);
  167. return CompletedTasks.True;
  168. }
  169. completionSource.Reset();
  170. return new UniTask<bool>(this, completionSource.Version);
  171. }
  172. public UniTask DisposeAsync()
  173. {
  174. if (!disposed)
  175. {
  176. disposed = true;
  177. TaskTracker.RemoveTracking(this);
  178. }
  179. return default;
  180. }
  181. public bool MoveNext()
  182. {
  183. if (disposed || cancellationToken.IsCancellationRequested || !target.TryGetTarget(out var t))
  184. {
  185. completionSource.TrySetResult(false);
  186. DisposeAsync().Forget();
  187. return false;
  188. }
  189. TProperty nextValue = default(TProperty);
  190. try
  191. {
  192. nextValue = propertySelector(t);
  193. if (equalityComparer.Equals(currentValue, nextValue))
  194. {
  195. return true;
  196. }
  197. }
  198. catch (Exception ex)
  199. {
  200. completionSource.TrySetException(ex);
  201. DisposeAsync().Forget();
  202. return false;
  203. }
  204. currentValue = nextValue;
  205. completionSource.TrySetResult(true);
  206. return true;
  207. }
  208. }
  209. }
  210. }