ListObservable.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the Apache 2.0 License.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Reactive.Disposables;
  7. using System.Reactive.Linq;
  8. using System.Reactive.Subjects;
  9. namespace System.Reactive
  10. {
  11. /// <summary>
  12. /// Represents an object that retains the elements of the observable sequence and signals the end of the sequence.
  13. /// </summary>
  14. /// <typeparam name="T">The type of elements received from the source sequence.</typeparam>
  15. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "By design; Observable suffix takes precedence.")]
  16. [Experimental]
  17. public class ListObservable<T> : IList<T>, IObservable<object>
  18. {
  19. IDisposable subscription;
  20. AsyncSubject<object> subject = new AsyncSubject<object>();
  21. List<T> results = new List<T>();
  22. /// <summary>
  23. /// Constructs an object that retains the values of source and signals the end of the sequence.
  24. /// </summary>
  25. /// <param name="source">The observable sequence whose elements will be retained in the list.</param>
  26. /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
  27. public ListObservable(IObservable<T> source)
  28. {
  29. if (source == null)
  30. throw new ArgumentNullException("source");
  31. subscription = source.Subscribe(results.Add, subject.OnError, subject.OnCompleted);
  32. }
  33. void Wait()
  34. {
  35. subject.DefaultIfEmpty().Wait();
  36. }
  37. /// <summary>
  38. /// Returns the last value of the observable sequence.
  39. /// </summary>
  40. public T Value
  41. {
  42. get
  43. {
  44. Wait();
  45. if (results.Count == 0)
  46. throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
  47. return results[results.Count - 1];
  48. }
  49. }
  50. /// <summary>
  51. /// Determines the index of a specific item in the ListObservable.
  52. /// </summary>
  53. /// <param name="item">The element to determine the index for.</param>
  54. /// <returns>The index of the specified item in the list; -1 if not found.</returns>
  55. public int IndexOf(T item)
  56. {
  57. Wait();
  58. return results.IndexOf(item);
  59. }
  60. /// <summary>
  61. /// Inserts an item to the ListObservable at the specified index.
  62. /// </summary>
  63. /// <param name="index">The index to insert the item at.</param>
  64. /// <param name="item">The item to insert in the list.</param>
  65. public void Insert(int index, T item)
  66. {
  67. Wait();
  68. results.Insert(index, item);
  69. }
  70. /// <summary>
  71. /// Removes the ListObservable item at the specified index.
  72. /// </summary>
  73. /// <param name="index">The index of the item to remove.</param>
  74. public void RemoveAt(int index)
  75. {
  76. Wait();
  77. results.RemoveAt(index);
  78. }
  79. /// <summary>
  80. /// Gets or sets the element at the specified index.
  81. /// </summary>
  82. /// <param name="index">The index of the item to retrieve or set.</param>
  83. public T this[int index]
  84. {
  85. get
  86. {
  87. Wait();
  88. return results[index];
  89. }
  90. set
  91. {
  92. Wait();
  93. results[index] = value;
  94. }
  95. }
  96. /// <summary>
  97. /// Adds an item to the ListObservable.
  98. /// </summary>
  99. /// <param name="item">The item to add to the list.</param>
  100. public void Add(T item)
  101. {
  102. Wait();
  103. results.Add(item);
  104. }
  105. /// <summary>
  106. /// Removes all items from the ListObservable.
  107. /// </summary>
  108. public void Clear()
  109. {
  110. Wait();
  111. results.Clear();
  112. }
  113. /// <summary>
  114. /// Determines whether the ListObservable contains a specific value.
  115. /// </summary>
  116. /// <param name="item">The item to search for in the list.</param>
  117. /// <returns>true if found; false otherwise.</returns>
  118. public bool Contains(T item)
  119. {
  120. Wait();
  121. return results.Contains(item);
  122. }
  123. /// <summary>
  124. /// Copies the elements of the ListObservable to an System.Array, starting at a particular System.Array index.
  125. /// </summary>
  126. /// <param name="array">The array to copy elements to.</param>
  127. /// <param name="arrayIndex">The start index in the array to start copying elements to.</param>
  128. public void CopyTo(T[] array, int arrayIndex)
  129. {
  130. Wait();
  131. results.CopyTo(array, arrayIndex);
  132. }
  133. /// <summary>
  134. /// Gets the number of elements contained in the ListObservable.
  135. /// </summary>
  136. public int Count
  137. {
  138. get
  139. {
  140. Wait();
  141. return results.Count;
  142. }
  143. }
  144. /// <summary>
  145. /// Gets a value that indicates whether the ListObservable is read-only.
  146. /// </summary>
  147. public bool IsReadOnly
  148. {
  149. get { return false; }
  150. }
  151. /// <summary>
  152. /// Removes the first occurrence of a specific object from the ListObservable.
  153. /// </summary>
  154. /// <param name="item">The item to remove from the list.</param>
  155. /// <returns>true if the item was found; false otherwise.</returns>
  156. public bool Remove(T item)
  157. {
  158. Wait();
  159. return results.Remove(item);
  160. }
  161. /// <summary>
  162. /// Returns an enumerator that iterates through the collection.
  163. /// </summary>
  164. /// <returns>Enumerator over the list.</returns>
  165. public IEnumerator<T> GetEnumerator()
  166. {
  167. Wait();
  168. return results.GetEnumerator();
  169. }
  170. IEnumerator IEnumerable.GetEnumerator()
  171. {
  172. return this.GetEnumerator();
  173. }
  174. /// <summary>
  175. /// Subscribes an observer to the ListObservable which will be notified upon completion.
  176. /// </summary>
  177. /// <param name="observer">The observer to send completion or error messages to.</param>
  178. /// <returns>The disposable resource that can be used to unsubscribe.</returns>
  179. /// <exception cref="ArgumentNullException"><paramref name="observer"/> is null.</exception>
  180. public IDisposable Subscribe(IObserver<object> observer)
  181. {
  182. if (observer == null)
  183. throw new ArgumentNullException("observer");
  184. return StableCompositeDisposable.Create(subscription, subject.Subscribe(observer));
  185. }
  186. }
  187. }