644 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			644 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C#
		
	
	
	
| using System;
 | |
| using System.Threading;
 | |
| 
 | |
| namespace Cysharp.Threading.Tasks
 | |
| {
 | |
|     public interface IReadOnlyAsyncReactiveProperty<T> : IUniTaskAsyncEnumerable<T>
 | |
|     {
 | |
|         T Value { get; }
 | |
|         IUniTaskAsyncEnumerable<T> WithoutCurrent();
 | |
|         UniTask<T> WaitAsync(CancellationToken cancellationToken = default);
 | |
|     }
 | |
| 
 | |
|     public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>
 | |
|     {
 | |
|         new T Value { get; set; }
 | |
|     }
 | |
| 
 | |
|     [Serializable]
 | |
|     public class AsyncReactiveProperty<T> : IAsyncReactiveProperty<T>, IDisposable
 | |
|     {
 | |
|         TriggerEvent<T> triggerEvent;
 | |
| 
 | |
| #if UNITY_2018_3_OR_NEWER
 | |
|         [UnityEngine.SerializeField]
 | |
| #endif
 | |
|         T latestValue;
 | |
| 
 | |
|         public T Value
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return latestValue;
 | |
|             }
 | |
|             set
 | |
|             {
 | |
|                 this.latestValue = value;
 | |
|                 triggerEvent.SetResult(value);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public AsyncReactiveProperty(T value)
 | |
|         {
 | |
|             this.latestValue = value;
 | |
|             this.triggerEvent = default;
 | |
|         }
 | |
| 
 | |
|         public IUniTaskAsyncEnumerable<T> WithoutCurrent()
 | |
|         {
 | |
|             return new WithoutCurrentEnumerable(this);
 | |
|         }
 | |
| 
 | |
|         public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
 | |
|         {
 | |
|             return new Enumerator(this, cancellationToken, true);
 | |
|         }
 | |
| 
 | |
|         public void Dispose()
 | |
|         {
 | |
|             triggerEvent.SetCompleted();
 | |
|         }
 | |
| 
 | |
|         public static implicit operator T(AsyncReactiveProperty<T> value)
 | |
|         {
 | |
|             return value.Value;
 | |
|         }
 | |
| 
 | |
|         public override string ToString()
 | |
|         {
 | |
|             if (isValueType) return latestValue.ToString();
 | |
|             return latestValue?.ToString();
 | |
|         }
 | |
| 
 | |
|         public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
 | |
|         {
 | |
|             return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
 | |
|         }
 | |
| 
 | |
|         static bool isValueType;
 | |
| 
 | |
|         static AsyncReactiveProperty()
 | |
|         {
 | |
|             isValueType = typeof(T).IsValueType;
 | |
|         }
 | |
| 
 | |
|         sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
 | |
|         {
 | |
|             static Action<object> cancellationCallback = CancellationCallback;
 | |
| 
 | |
|             static TaskPool<WaitAsyncSource> pool;
 | |
|             WaitAsyncSource nextNode;
 | |
|             ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
 | |
| 
 | |
|             static WaitAsyncSource()
 | |
|             {
 | |
|                 TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
 | |
|             }
 | |
| 
 | |
|             AsyncReactiveProperty<T> parent;
 | |
|             CancellationToken cancellationToken;
 | |
|             CancellationTokenRegistration cancellationTokenRegistration;
 | |
|             UniTaskCompletionSourceCore<T> core;
 | |
| 
 | |
|             WaitAsyncSource()
 | |
|             {
 | |
|             }
 | |
| 
 | |
|             public static IUniTaskSource<T> Create(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
 | |
|             {
 | |
|                 if (cancellationToken.IsCancellationRequested)
 | |
|                 {
 | |
|                     return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
 | |
|                 }
 | |
| 
 | |
|                 if (!pool.TryPop(out var result))
 | |
|                 {
 | |
|                     result = new WaitAsyncSource();
 | |
|                 }
 | |
| 
 | |
|                 result.parent = parent;
 | |
|                 result.cancellationToken = cancellationToken;
 | |
| 
 | |
|                 if (cancellationToken.CanBeCanceled)
 | |
|                 {
 | |
|                     result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
 | |
|                 }
 | |
| 
 | |
|                 result.parent.triggerEvent.Add(result);
 | |
| 
 | |
|                 TaskTracker.TrackActiveTask(result, 3);
 | |
| 
 | |
|                 token = result.core.Version;
 | |
|                 return result;
 | |
|             }
 | |
| 
 | |
|             bool TryReturn()
 | |
|             {
 | |
|                 TaskTracker.RemoveTracking(this);
 | |
|                 core.Reset();
 | |
|                 cancellationTokenRegistration.Dispose();
 | |
|                 cancellationTokenRegistration = default;
 | |
|                 parent.triggerEvent.Remove(this);
 | |
|                 parent = null;
 | |
|                 cancellationToken = default;
 | |
|                 return pool.TryPush(this);
 | |
|             }
 | |
| 
 | |
|             static void CancellationCallback(object state)
 | |
|             {
 | |
|                 var self = (WaitAsyncSource)state;
 | |
|                 self.OnCanceled(self.cancellationToken);
 | |
|             }
 | |
| 
 | |
|             // IUniTaskSource
 | |
| 
 | |
|             public T GetResult(short token)
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     return core.GetResult(token);
 | |
|                 }
 | |
|                 finally
 | |
|                 {
 | |
|                     TryReturn();
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             void IUniTaskSource.GetResult(short token)
 | |
|             {
 | |
|                 GetResult(token);
 | |
|             }
 | |
| 
 | |
|             public void OnCompleted(Action<object> continuation, object state, short token)
 | |
|             {
 | |
|                 core.OnCompleted(continuation, state, token);
 | |
|             }
 | |
| 
 | |
|             public UniTaskStatus GetStatus(short token)
 | |
|             {
 | |
|                 return core.GetStatus(token);
 | |
|             }
 | |
| 
 | |
|             public UniTaskStatus UnsafeGetStatus()
 | |
|             {
 | |
|                 return core.UnsafeGetStatus();
 | |
|             }
 | |
| 
 | |
|             // ITriggerHandler
 | |
| 
 | |
|             ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
 | |
|             ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
 | |
| 
 | |
|             public void OnCanceled(CancellationToken cancellationToken)
 | |
|             {
 | |
|                 core.TrySetCanceled(cancellationToken);
 | |
|             }
 | |
| 
 | |
|             public void OnCompleted()
 | |
|             {
 | |
|                 // Complete as Cancel.
 | |
|                 core.TrySetCanceled(CancellationToken.None);
 | |
|             }
 | |
| 
 | |
|             public void OnError(Exception ex)
 | |
|             {
 | |
|                 core.TrySetException(ex);
 | |
|             }
 | |
| 
 | |
|             public void OnNext(T value)
 | |
|             {
 | |
|                 core.TrySetResult(value);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
 | |
|         {
 | |
|             readonly AsyncReactiveProperty<T> parent;
 | |
| 
 | |
|             public WithoutCurrentEnumerable(AsyncReactiveProperty<T> parent)
 | |
|             {
 | |
|                 this.parent = parent;
 | |
|             }
 | |
| 
 | |
|             public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
 | |
|             {
 | |
|                 return new Enumerator(parent, cancellationToken, false);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
 | |
|         {
 | |
|             static Action<object> cancellationCallback = CancellationCallback;
 | |
| 
 | |
|             readonly AsyncReactiveProperty<T> parent;
 | |
|             readonly CancellationToken cancellationToken;
 | |
|             readonly CancellationTokenRegistration cancellationTokenRegistration;
 | |
|             T value;
 | |
|             bool isDisposed;
 | |
|             bool firstCall;
 | |
| 
 | |
|             public Enumerator(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
 | |
|             {
 | |
|                 this.parent = parent;
 | |
|                 this.cancellationToken = cancellationToken;
 | |
|                 this.firstCall = publishCurrentValue;
 | |
| 
 | |
|                 parent.triggerEvent.Add(this);
 | |
|                 TaskTracker.TrackActiveTask(this, 3);
 | |
| 
 | |
|                 if (cancellationToken.CanBeCanceled)
 | |
|                 {
 | |
|                     cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public T Current => value;
 | |
| 
 | |
|             ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
 | |
|             ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
 | |
| 
 | |
|             public UniTask<bool> MoveNextAsync()
 | |
|             {
 | |
|                 // raise latest value on first call.
 | |
|                 if (firstCall)
 | |
|                 {
 | |
|                     firstCall = false;
 | |
|                     value = parent.Value;
 | |
|                     return CompletedTasks.True;
 | |
|                 }
 | |
| 
 | |
|                 completionSource.Reset();
 | |
|                 return new UniTask<bool>(this, completionSource.Version);
 | |
|             }
 | |
| 
 | |
|             public UniTask DisposeAsync()
 | |
|             {
 | |
|                 if (!isDisposed)
 | |
|                 {
 | |
|                     isDisposed = true;
 | |
|                     TaskTracker.RemoveTracking(this);
 | |
|                     completionSource.TrySetCanceled(cancellationToken);
 | |
|                     parent.triggerEvent.Remove(this);
 | |
|                 }
 | |
|                 return default;
 | |
|             }
 | |
| 
 | |
|             public void OnNext(T value)
 | |
|             {
 | |
|                 this.value = value;
 | |
|                 completionSource.TrySetResult(true);
 | |
|             }
 | |
| 
 | |
|             public void OnCanceled(CancellationToken cancellationToken)
 | |
|             {
 | |
|                 DisposeAsync().Forget();
 | |
|             }
 | |
| 
 | |
|             public void OnCompleted()
 | |
|             {
 | |
|                 completionSource.TrySetResult(false);
 | |
|             }
 | |
| 
 | |
|             public void OnError(Exception ex)
 | |
|             {
 | |
|                 completionSource.TrySetException(ex);
 | |
|             }
 | |
| 
 | |
|             static void CancellationCallback(object state)
 | |
|             {
 | |
|                 var self = (Enumerator)state;
 | |
|                 self.DisposeAsync().Forget();
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public class ReadOnlyAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>, IDisposable
 | |
|     {
 | |
|         TriggerEvent<T> triggerEvent;
 | |
| 
 | |
|         T latestValue;
 | |
|         IUniTaskAsyncEnumerator<T> enumerator;
 | |
| 
 | |
|         public T Value
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return latestValue;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public ReadOnlyAsyncReactiveProperty(T initialValue, IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
 | |
|         {
 | |
|             latestValue = initialValue;
 | |
|             ConsumeEnumerator(source, cancellationToken).Forget();
 | |
|         }
 | |
| 
 | |
|         public ReadOnlyAsyncReactiveProperty(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
 | |
|         {
 | |
|             ConsumeEnumerator(source, cancellationToken).Forget();
 | |
|         }
 | |
| 
 | |
|         async UniTaskVoid ConsumeEnumerator(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
 | |
|         {
 | |
|             enumerator = source.GetAsyncEnumerator(cancellationToken);
 | |
|             try
 | |
|             {
 | |
|                 while (await enumerator.MoveNextAsync())
 | |
|                 {
 | |
|                     var value = enumerator.Current;
 | |
|                     this.latestValue = value;
 | |
|                     triggerEvent.SetResult(value);
 | |
|                 }
 | |
|             }
 | |
|             finally
 | |
|             {
 | |
|                 await enumerator.DisposeAsync();
 | |
|                 enumerator = null;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public IUniTaskAsyncEnumerable<T> WithoutCurrent()
 | |
|         {
 | |
|             return new WithoutCurrentEnumerable(this);
 | |
|         }
 | |
| 
 | |
|         public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
 | |
|         {
 | |
|             return new Enumerator(this, cancellationToken, true);
 | |
|         }
 | |
| 
 | |
|         public void Dispose()
 | |
|         {
 | |
|             if (enumerator != null)
 | |
|             {
 | |
|                 enumerator.DisposeAsync().Forget();
 | |
|             }
 | |
| 
 | |
|             triggerEvent.SetCompleted();
 | |
|         }
 | |
| 
 | |
|         public static implicit operator T(ReadOnlyAsyncReactiveProperty<T> value)
 | |
|         {
 | |
|             return value.Value;
 | |
|         }
 | |
| 
 | |
|         public override string ToString()
 | |
|         {
 | |
|             if (isValueType) return latestValue.ToString();
 | |
|             return latestValue?.ToString();
 | |
|         }
 | |
| 
 | |
|         public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
 | |
|         {
 | |
|             return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
 | |
|         }
 | |
| 
 | |
|         static bool isValueType;
 | |
| 
 | |
|         static ReadOnlyAsyncReactiveProperty()
 | |
|         {
 | |
|             isValueType = typeof(T).IsValueType;
 | |
|         }
 | |
| 
 | |
|         sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
 | |
|         {
 | |
|             static Action<object> cancellationCallback = CancellationCallback;
 | |
| 
 | |
|             static TaskPool<WaitAsyncSource> pool;
 | |
|             WaitAsyncSource nextNode;
 | |
|             ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
 | |
| 
 | |
|             static WaitAsyncSource()
 | |
|             {
 | |
|                 TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
 | |
|             }
 | |
| 
 | |
|             ReadOnlyAsyncReactiveProperty<T> parent;
 | |
|             CancellationToken cancellationToken;
 | |
|             CancellationTokenRegistration cancellationTokenRegistration;
 | |
|             UniTaskCompletionSourceCore<T> core;
 | |
| 
 | |
|             WaitAsyncSource()
 | |
|             {
 | |
|             }
 | |
| 
 | |
|             public static IUniTaskSource<T> Create(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
 | |
|             {
 | |
|                 if (cancellationToken.IsCancellationRequested)
 | |
|                 {
 | |
|                     return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
 | |
|                 }
 | |
| 
 | |
|                 if (!pool.TryPop(out var result))
 | |
|                 {
 | |
|                     result = new WaitAsyncSource();
 | |
|                 }
 | |
| 
 | |
|                 result.parent = parent;
 | |
|                 result.cancellationToken = cancellationToken;
 | |
| 
 | |
|                 if (cancellationToken.CanBeCanceled)
 | |
|                 {
 | |
|                     result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
 | |
|                 }
 | |
| 
 | |
|                 result.parent.triggerEvent.Add(result);
 | |
| 
 | |
|                 TaskTracker.TrackActiveTask(result, 3);
 | |
| 
 | |
|                 token = result.core.Version;
 | |
|                 return result;
 | |
|             }
 | |
| 
 | |
|             bool TryReturn()
 | |
|             {
 | |
|                 TaskTracker.RemoveTracking(this);
 | |
|                 core.Reset();
 | |
|                 cancellationTokenRegistration.Dispose();
 | |
|                 cancellationTokenRegistration = default;
 | |
|                 parent.triggerEvent.Remove(this);
 | |
|                 parent = null;
 | |
|                 cancellationToken = default;
 | |
|                 return pool.TryPush(this);
 | |
|             }
 | |
| 
 | |
|             static void CancellationCallback(object state)
 | |
|             {
 | |
|                 var self = (WaitAsyncSource)state;
 | |
|                 self.OnCanceled(self.cancellationToken);
 | |
|             }
 | |
| 
 | |
|             // IUniTaskSource
 | |
| 
 | |
|             public T GetResult(short token)
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     return core.GetResult(token);
 | |
|                 }
 | |
|                 finally
 | |
|                 {
 | |
|                     TryReturn();
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             void IUniTaskSource.GetResult(short token)
 | |
|             {
 | |
|                 GetResult(token);
 | |
|             }
 | |
| 
 | |
|             public void OnCompleted(Action<object> continuation, object state, short token)
 | |
|             {
 | |
|                 core.OnCompleted(continuation, state, token);
 | |
|             }
 | |
| 
 | |
|             public UniTaskStatus GetStatus(short token)
 | |
|             {
 | |
|                 return core.GetStatus(token);
 | |
|             }
 | |
| 
 | |
|             public UniTaskStatus UnsafeGetStatus()
 | |
|             {
 | |
|                 return core.UnsafeGetStatus();
 | |
|             }
 | |
| 
 | |
|             // ITriggerHandler
 | |
| 
 | |
|             ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
 | |
|             ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
 | |
| 
 | |
|             public void OnCanceled(CancellationToken cancellationToken)
 | |
|             {
 | |
|                 core.TrySetCanceled(cancellationToken);
 | |
|             }
 | |
| 
 | |
|             public void OnCompleted()
 | |
|             {
 | |
|                 // Complete as Cancel.
 | |
|                 core.TrySetCanceled(CancellationToken.None);
 | |
|             }
 | |
| 
 | |
|             public void OnError(Exception ex)
 | |
|             {
 | |
|                 core.TrySetException(ex);
 | |
|             }
 | |
| 
 | |
|             public void OnNext(T value)
 | |
|             {
 | |
|                 core.TrySetResult(value);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
 | |
|         {
 | |
|             readonly ReadOnlyAsyncReactiveProperty<T> parent;
 | |
| 
 | |
|             public WithoutCurrentEnumerable(ReadOnlyAsyncReactiveProperty<T> parent)
 | |
|             {
 | |
|                 this.parent = parent;
 | |
|             }
 | |
| 
 | |
|             public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
 | |
|             {
 | |
|                 return new Enumerator(parent, cancellationToken, false);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
 | |
|         {
 | |
|             static Action<object> cancellationCallback = CancellationCallback;
 | |
| 
 | |
|             readonly ReadOnlyAsyncReactiveProperty<T> parent;
 | |
|             readonly CancellationToken cancellationToken;
 | |
|             readonly CancellationTokenRegistration cancellationTokenRegistration;
 | |
|             T value;
 | |
|             bool isDisposed;
 | |
|             bool firstCall;
 | |
| 
 | |
|             public Enumerator(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
 | |
|             {
 | |
|                 this.parent = parent;
 | |
|                 this.cancellationToken = cancellationToken;
 | |
|                 this.firstCall = publishCurrentValue;
 | |
| 
 | |
|                 parent.triggerEvent.Add(this);
 | |
|                 TaskTracker.TrackActiveTask(this, 3);
 | |
| 
 | |
|                 if (cancellationToken.CanBeCanceled)
 | |
|                 {
 | |
|                     cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public T Current => value;
 | |
|             ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
 | |
|             ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
 | |
| 
 | |
|             public UniTask<bool> MoveNextAsync()
 | |
|             {
 | |
|                 // raise latest value on first call.
 | |
|                 if (firstCall)
 | |
|                 {
 | |
|                     firstCall = false;
 | |
|                     value = parent.Value;
 | |
|                     return CompletedTasks.True;
 | |
|                 }
 | |
| 
 | |
|                 completionSource.Reset();
 | |
|                 return new UniTask<bool>(this, completionSource.Version);
 | |
|             }
 | |
| 
 | |
|             public UniTask DisposeAsync()
 | |
|             {
 | |
|                 if (!isDisposed)
 | |
|                 {
 | |
|                     isDisposed = true;
 | |
|                     TaskTracker.RemoveTracking(this);
 | |
|                     completionSource.TrySetCanceled(cancellationToken);
 | |
|                     parent.triggerEvent.Remove(this);
 | |
|                 }
 | |
|                 return default;
 | |
|             }
 | |
| 
 | |
|             public void OnNext(T value)
 | |
|             {
 | |
|                 this.value = value;
 | |
|                 completionSource.TrySetResult(true);
 | |
|             }
 | |
| 
 | |
|             public void OnCanceled(CancellationToken cancellationToken)
 | |
|             {
 | |
|                 DisposeAsync().Forget();
 | |
|             }
 | |
| 
 | |
|             public void OnCompleted()
 | |
|             {
 | |
|                 completionSource.TrySetResult(false);
 | |
|             }
 | |
| 
 | |
|             public void OnError(Exception ex)
 | |
|             {
 | |
|                 completionSource.TrySetException(ex);
 | |
|             }
 | |
| 
 | |
|             static void CancellationCallback(object state)
 | |
|             {
 | |
|                 var self = (Enumerator)state;
 | |
|                 self.DisposeAsync().Forget();
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public static class StateExtensions
 | |
|     {
 | |
|         public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
 | |
|         {
 | |
|             return new ReadOnlyAsyncReactiveProperty<T>(source, cancellationToken);
 | |
|         }
 | |
| 
 | |
|         public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, T initialValue, CancellationToken cancellationToken)
 | |
|         {
 | |
|             return new ReadOnlyAsyncReactiveProperty<T>(initialValue, source, cancellationToken);
 | |
|         }
 | |
|     }
 | |
| } |