ObservableEx.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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.Generic;
  5. using System.Reactive.Concurrency;
  6. namespace System.Reactive.Linq
  7. {
  8. /// <summary>
  9. /// Provides a set of static methods for writing in-memory queries over observable sequences.
  10. /// </summary>
  11. public static class ObservableEx
  12. {
  13. #pragma warning disable IDE1006 // Naming Styles: 3rd party code is known to reflect for this specific field name
  14. private static IQueryLanguageEx s_impl = QueryServices.GetQueryImpl<IQueryLanguageEx>(new QueryLanguageEx());
  15. #pragma warning restore IDE1006 // Naming Styles
  16. #region Create
  17. /// <summary>
  18. /// Subscribes to each observable sequence returned by the iteratorMethod in sequence and returns the observable sequence of values sent to the observer given to the iteratorMethod.
  19. /// </summary>
  20. /// <typeparam name="TResult">The type of the elements in the produced sequence.</typeparam>
  21. /// <param name="iteratorMethod">Iterator method that produces elements in the resulting sequence by calling the given observer.</param>
  22. /// <returns>An observable sequence obtained by running the iterator and returning the elements that were sent to the observer.</returns>
  23. /// <exception cref="ArgumentNullException"><paramref name="iteratorMethod"/> is null.</exception>
  24. [Experimental]
  25. public static IObservable<TResult> Create<TResult>(Func<IObserver<TResult>, IEnumerable<IObservable<object>>> iteratorMethod)
  26. {
  27. if (iteratorMethod == null)
  28. {
  29. throw new ArgumentNullException(nameof(iteratorMethod));
  30. }
  31. return s_impl.Create(iteratorMethod);
  32. }
  33. /// <summary>
  34. /// Subscribes to each observable sequence returned by the iteratorMethod in sequence and produces a Unit value on the resulting sequence for each step of the iteration.
  35. /// </summary>
  36. /// <param name="iteratorMethod">Iterator method that drives the resulting observable sequence.</param>
  37. /// <returns>An observable sequence obtained by running the iterator and returning Unit values for each iteration step.</returns>
  38. /// <exception cref="ArgumentNullException"><paramref name="iteratorMethod"/> is null.</exception>
  39. [Experimental]
  40. public static IObservable<Unit> Create(Func<IEnumerable<IObservable<object>>> iteratorMethod)
  41. {
  42. if (iteratorMethod == null)
  43. {
  44. throw new ArgumentNullException(nameof(iteratorMethod));
  45. }
  46. return s_impl.Create(iteratorMethod);
  47. }
  48. #endregion
  49. #region Expand
  50. /// <summary>
  51. /// Expands an observable sequence by recursively invoking selector, using the specified scheduler to enumerate the queue of obtained sequences.
  52. /// </summary>
  53. /// <typeparam name="TSource">The type of the elements in the source sequence and each of the recursively expanded sources obtained by running the selector function.</typeparam>
  54. /// <param name="source">Source sequence with the initial elements.</param>
  55. /// <param name="selector">Selector function to invoke for each produced element, resulting in another sequence to which the selector will be invoked recursively again.</param>
  56. /// <param name="scheduler">Scheduler on which to perform the expansion by enumerating the internal queue of obtained sequences.</param>
  57. /// <returns>An observable sequence containing all the elements produced by the recursive expansion.</returns>
  58. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="selector"/> or <paramref name="scheduler"/> is null.</exception>
  59. [Experimental]
  60. public static IObservable<TSource> Expand<TSource>(this IObservable<TSource> source, Func<TSource, IObservable<TSource>> selector, IScheduler scheduler)
  61. {
  62. if (source == null)
  63. {
  64. throw new ArgumentNullException(nameof(source));
  65. }
  66. if (selector == null)
  67. {
  68. throw new ArgumentNullException(nameof(selector));
  69. }
  70. if (scheduler == null)
  71. {
  72. throw new ArgumentNullException(nameof(scheduler));
  73. }
  74. return s_impl.Expand(source, selector, scheduler);
  75. }
  76. /// <summary>
  77. /// Expands an observable sequence by recursively invoking selector.
  78. /// </summary>
  79. /// <typeparam name="TSource">The type of the elements in the source sequence and each of the recursively expanded sources obtained by running the selector function.</typeparam>
  80. /// <param name="source">Source sequence with the initial elements.</param>
  81. /// <param name="selector">Selector function to invoke for each produced element, resulting in another sequence to which the selector will be invoked recursively again.</param>
  82. /// <returns>An observable sequence containing all the elements produced by the recursive expansion.</returns>
  83. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="selector"/> is null.</exception>
  84. [Experimental]
  85. public static IObservable<TSource> Expand<TSource>(this IObservable<TSource> source, Func<TSource, IObservable<TSource>> selector)
  86. {
  87. if (source == null)
  88. {
  89. throw new ArgumentNullException(nameof(source));
  90. }
  91. if (selector == null)
  92. {
  93. throw new ArgumentNullException(nameof(selector));
  94. }
  95. return s_impl.Expand(source, selector);
  96. }
  97. #endregion
  98. #region ForkJoin
  99. /// <summary>
  100. /// Runs two observable sequences in parallel and combines their last elements.
  101. /// </summary>
  102. /// <typeparam name="TSource1">The type of the elements in the first source sequence.</typeparam>
  103. /// <typeparam name="TSource2">The type of the elements in the second source sequence.</typeparam>
  104. /// <typeparam name="TResult">The type of the elements in the result sequence, returned by the selector function.</typeparam>
  105. /// <param name="first">First observable sequence.</param>
  106. /// <param name="second">Second observable sequence.</param>
  107. /// <param name="resultSelector">Result selector function to invoke with the last elements of both sequences.</param>
  108. /// <returns>An observable sequence with the result of calling the selector function with the last elements of both input sequences.</returns>
  109. /// <exception cref="ArgumentNullException"><paramref name="first"/> or <paramref name="second"/> or <paramref name="resultSelector"/> is null.</exception>
  110. [Experimental]
  111. public static IObservable<TResult> ForkJoin<TSource1, TSource2, TResult>(this IObservable<TSource1> first, IObservable<TSource2> second, Func<TSource1, TSource2, TResult> resultSelector)
  112. {
  113. if (first == null)
  114. {
  115. throw new ArgumentNullException(nameof(first));
  116. }
  117. if (second == null)
  118. {
  119. throw new ArgumentNullException(nameof(second));
  120. }
  121. if (resultSelector == null)
  122. {
  123. throw new ArgumentNullException(nameof(resultSelector));
  124. }
  125. return s_impl.ForkJoin(first, second, resultSelector);
  126. }
  127. /// <summary>
  128. /// Runs all specified observable sequences in parallel and collects their last elements.
  129. /// </summary>
  130. /// <typeparam name="TSource">The type of the elements in the source sequences.</typeparam>
  131. /// <param name="sources">Observable sequence to collect the last elements for.</param>
  132. /// <returns>An observable sequence with an array collecting the last elements of all the input sequences.</returns>
  133. /// <exception cref="ArgumentNullException"><paramref name="sources"/> is null.</exception>
  134. [Experimental]
  135. public static IObservable<TSource[]> ForkJoin<TSource>(params IObservable<TSource>[] sources)
  136. {
  137. if (sources == null)
  138. {
  139. throw new ArgumentNullException(nameof(sources));
  140. }
  141. return s_impl.ForkJoin(sources);
  142. }
  143. /// <summary>
  144. /// Runs all observable sequences in the enumerable sources sequence in parallel and collect their last elements.
  145. /// </summary>
  146. /// <typeparam name="TSource">The type of the elements in the source sequences.</typeparam>
  147. /// <param name="sources">Observable sequence to collect the last elements for.</param>
  148. /// <returns>An observable sequence with an array collecting the last elements of all the input sequences.</returns>
  149. /// <exception cref="ArgumentNullException"><paramref name="sources"/> is null.</exception>
  150. [Experimental]
  151. public static IObservable<TSource[]> ForkJoin<TSource>(this IEnumerable<IObservable<TSource>> sources)
  152. {
  153. if (sources == null)
  154. {
  155. throw new ArgumentNullException(nameof(sources));
  156. }
  157. return s_impl.ForkJoin(sources);
  158. }
  159. #endregion
  160. #region Let
  161. /// <summary>
  162. /// Returns an observable sequence that is the result of invoking the selector on the source sequence, without sharing subscriptions.
  163. /// This operator allows for a fluent style of writing queries that use the same sequence multiple times.
  164. /// </summary>
  165. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  166. /// <typeparam name="TResult">The type of the elements in the result sequence.</typeparam>
  167. /// <param name="source">Source sequence that will be shared in the selector function.</param>
  168. /// <param name="selector">Selector function which can use the source sequence as many times as needed, without sharing subscriptions to the source sequence.</param>
  169. /// <returns>An observable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function.</returns>
  170. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="selector"/> is null.</exception>
  171. [Experimental]
  172. public static IObservable<TResult> Let<TSource, TResult>(this IObservable<TSource> source, Func<IObservable<TSource>, IObservable<TResult>> selector)
  173. {
  174. if (source == null)
  175. {
  176. throw new ArgumentNullException(nameof(source));
  177. }
  178. if (selector == null)
  179. {
  180. throw new ArgumentNullException(nameof(selector));
  181. }
  182. return s_impl.Let(source, selector);
  183. }
  184. #endregion
  185. #region ManySelect
  186. /// <summary>
  187. /// Comonadic bind operator.
  188. /// </summary>
  189. [Experimental]
  190. public static IObservable<TResult> ManySelect<TSource, TResult>(this IObservable<TSource> source, Func<IObservable<TSource>, TResult> selector, IScheduler scheduler)
  191. {
  192. if (source == null)
  193. {
  194. throw new ArgumentNullException(nameof(source));
  195. }
  196. if (selector == null)
  197. {
  198. throw new ArgumentNullException(nameof(selector));
  199. }
  200. if (scheduler == null)
  201. {
  202. throw new ArgumentNullException(nameof(scheduler));
  203. }
  204. return s_impl.ManySelect(source, selector, scheduler);
  205. }
  206. /// <summary>
  207. /// Comonadic bind operator.
  208. /// </summary>
  209. [Experimental]
  210. public static IObservable<TResult> ManySelect<TSource, TResult>(this IObservable<TSource> source, Func<IObservable<TSource>, TResult> selector)
  211. {
  212. if (source == null)
  213. {
  214. throw new ArgumentNullException(nameof(source));
  215. }
  216. if (selector == null)
  217. {
  218. throw new ArgumentNullException(nameof(selector));
  219. }
  220. return s_impl.ManySelect(source, selector);
  221. }
  222. #endregion
  223. #region ToListObservable
  224. /// <summary>
  225. /// Immediately subscribes to source and retains the elements in the observable sequence.
  226. /// </summary>
  227. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  228. /// <param name="source">Source sequence.</param>
  229. /// <returns>Object that's both an observable sequence and a list which can be used to access the source sequence's elements.</returns>
  230. /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
  231. [Experimental]
  232. public static ListObservable<TSource> ToListObservable<TSource>(this IObservable<TSource> source)
  233. {
  234. if (source == null)
  235. {
  236. throw new ArgumentNullException(nameof(source));
  237. }
  238. return s_impl.ToListObservable(source);
  239. }
  240. #endregion
  241. }
  242. }