Observable.Events.cs 92 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279
  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.Reactive.Concurrency;
  5. using System.Threading;
  6. namespace System.Reactive.Linq
  7. {
  8. public static partial class Observable
  9. {
  10. #region + FromEventPattern +
  11. #region Strongly typed
  12. #region Action<EventHandler>
  13. /// <summary>
  14. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler"/>, to an observable sequence.
  15. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  16. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  17. /// </summary>
  18. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  19. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  20. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  21. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  22. /// <remarks>
  23. /// <para>
  24. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  25. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  26. /// </para>
  27. /// <para>
  28. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  29. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  30. /// </para>
  31. /// <para>
  32. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  33. /// making the Subscribe or Dispose call, respectively.
  34. /// </para>
  35. /// <para>
  36. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  37. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  38. /// more concise and easier to understand.
  39. /// </para>
  40. /// </remarks>
  41. /// <seealso cref="Observable.ToEventPattern"/>
  42. public static IObservable<EventPattern<object>> FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler)
  43. {
  44. if (addHandler == null)
  45. throw new ArgumentNullException(nameof(addHandler));
  46. if (removeHandler == null)
  47. throw new ArgumentNullException(nameof(removeHandler));
  48. return s_impl.FromEventPattern(addHandler, removeHandler);
  49. }
  50. /// <summary>
  51. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler"/>, to an observable sequence.
  52. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  53. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  54. /// </summary>
  55. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  56. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  57. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  58. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  59. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  60. /// <remarks>
  61. /// <para>
  62. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  63. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  64. /// </para>
  65. /// <para>
  66. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  67. /// accessed from the same context, as required by some UI frameworks.
  68. /// </para>
  69. /// <para>
  70. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  71. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  72. /// parameter. For more information, see the remarks section on those overloads.
  73. /// </para>
  74. /// </remarks>
  75. /// <seealso cref="Observable.ToEventPattern"/>
  76. public static IObservable<EventPattern<object>> FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler, IScheduler scheduler)
  77. {
  78. if (addHandler == null)
  79. throw new ArgumentNullException(nameof(addHandler));
  80. if (removeHandler == null)
  81. throw new ArgumentNullException(nameof(removeHandler));
  82. if (scheduler == null)
  83. throw new ArgumentNullException(nameof(scheduler));
  84. return s_impl.FromEventPattern(addHandler, removeHandler, scheduler);
  85. }
  86. #endregion
  87. #region Action<TDelegate>
  88. /// <summary>
  89. /// Converts a .NET event, conforming to the standard .NET event pattern based on a supplied event delegate type, to an observable sequence.
  90. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  91. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  92. /// </summary>
  93. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  94. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  95. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  96. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  97. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  98. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  99. /// <remarks>
  100. /// <para>
  101. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  102. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  103. /// </para>
  104. /// <para>
  105. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  106. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  107. /// </para>
  108. /// <para>
  109. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  110. /// making the Subscribe or Dispose call, respectively.
  111. /// </para>
  112. /// <para>
  113. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  114. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  115. /// more concise and easier to understand.
  116. /// </para>
  117. /// </remarks>
  118. /// <seealso cref="Observable.ToEventPattern"/>
  119. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
  120. {
  121. if (addHandler == null)
  122. throw new ArgumentNullException(nameof(addHandler));
  123. if (removeHandler == null)
  124. throw new ArgumentNullException(nameof(removeHandler));
  125. return s_impl.FromEventPattern<TDelegate, TEventArgs>(addHandler, removeHandler);
  126. }
  127. /// <summary>
  128. /// Converts a .NET event, conforming to the standard .NET event pattern based on a supplied event delegate type, to an observable sequence.
  129. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  130. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  131. /// </summary>
  132. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  133. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  134. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  135. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  136. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  137. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  138. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  139. /// <remarks>
  140. /// <para>
  141. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  142. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  143. /// </para>
  144. /// <para>
  145. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  146. /// accessed from the same context, as required by some UI frameworks.
  147. /// </para>
  148. /// <para>
  149. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  150. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  151. /// parameter. For more information, see the remarks section on those overloads.
  152. /// </para>
  153. /// </remarks>
  154. /// <seealso cref="Observable.ToEventPattern"/>
  155. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler)
  156. {
  157. if (addHandler == null)
  158. throw new ArgumentNullException(nameof(addHandler));
  159. if (removeHandler == null)
  160. throw new ArgumentNullException(nameof(removeHandler));
  161. if (scheduler == null)
  162. throw new ArgumentNullException(nameof(scheduler));
  163. return s_impl.FromEventPattern<TDelegate, TEventArgs>(addHandler, removeHandler, scheduler);
  164. }
  165. /// <summary>
  166. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler{TEventArgs}"/>, to an observable sequence.
  167. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  168. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  169. /// </summary>
  170. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  171. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  172. /// <param name="conversion">A function used to convert the given event handler to a delegate compatible with the underlying .NET event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>
  173. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  174. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  175. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  176. /// <exception cref="ArgumentNullException"><paramref name="conversion"/> or <paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  177. /// <remarks>
  178. /// <para>
  179. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  180. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  181. /// </para>
  182. /// <para>
  183. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  184. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  185. /// </para>
  186. /// <para>
  187. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  188. /// making the Subscribe or Dispose call, respectively.
  189. /// </para>
  190. /// <para>
  191. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  192. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  193. /// more concise and easier to understand.
  194. /// </para>
  195. /// </remarks>
  196. /// <seealso cref="Observable.ToEventPattern"/>
  197. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Func<EventHandler<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
  198. {
  199. if (conversion == null)
  200. throw new ArgumentNullException(nameof(conversion));
  201. if (addHandler == null)
  202. throw new ArgumentNullException(nameof(addHandler));
  203. if (removeHandler == null)
  204. throw new ArgumentNullException(nameof(removeHandler));
  205. return s_impl.FromEventPattern<TDelegate, TEventArgs>(conversion, addHandler, removeHandler);
  206. }
  207. /// <summary>
  208. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler{TEventArgs}"/>, to an observable sequence.
  209. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  210. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  211. /// </summary>
  212. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  213. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  214. /// <param name="conversion">A function used to convert the given event handler to a delegate compatible with the underlying .NET event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>
  215. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  216. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  217. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  218. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  219. /// <exception cref="ArgumentNullException"><paramref name="conversion"/> or <paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  220. /// <remarks>
  221. /// <para>
  222. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  223. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  224. /// </para>
  225. /// <para>
  226. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  227. /// accessed from the same context, as required by some UI frameworks.
  228. /// </para>
  229. /// <para>
  230. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  231. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  232. /// parameter. For more information, see the remarks section on those overloads.
  233. /// </para>
  234. /// </remarks>
  235. /// <seealso cref="Observable.ToEventPattern"/>
  236. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Func<EventHandler<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler)
  237. {
  238. if (conversion == null)
  239. throw new ArgumentNullException(nameof(conversion));
  240. if (addHandler == null)
  241. throw new ArgumentNullException(nameof(addHandler));
  242. if (removeHandler == null)
  243. throw new ArgumentNullException(nameof(removeHandler));
  244. if (scheduler == null)
  245. throw new ArgumentNullException(nameof(scheduler));
  246. return s_impl.FromEventPattern<TDelegate, TEventArgs>(conversion, addHandler, removeHandler, scheduler);
  247. }
  248. /// <summary>
  249. /// Converts a .NET event, conforming to the standard .NET event pattern based on a supplied event delegate type with a strongly typed sender parameter, to an observable sequence.
  250. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  251. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  252. /// </summary>
  253. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  254. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  255. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  256. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  257. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  258. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  259. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  260. /// <remarks>
  261. /// <para>
  262. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  263. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  264. /// </para>
  265. /// <para>
  266. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  267. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  268. /// </para>
  269. /// <para>
  270. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  271. /// making the Subscribe or Dispose call, respectively.
  272. /// </para>
  273. /// <para>
  274. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  275. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  276. /// more concise and easier to understand.
  277. /// </para>
  278. /// </remarks>
  279. /// <seealso cref="Observable.ToEventPattern"/>
  280. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TDelegate, TSender, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
  281. {
  282. if (addHandler == null)
  283. throw new ArgumentNullException(nameof(addHandler));
  284. if (removeHandler == null)
  285. throw new ArgumentNullException(nameof(removeHandler));
  286. return s_impl.FromEventPattern<TDelegate, TSender, TEventArgs>(addHandler, removeHandler);
  287. }
  288. /// <summary>
  289. /// Converts a .NET event, conforming to the standard .NET event pattern based on a supplied event delegate type with a strongly typed sender parameter, to an observable sequence.
  290. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  291. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  292. /// </summary>
  293. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  294. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  295. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  296. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  297. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  298. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  299. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  300. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  301. /// <remarks>
  302. /// <para>
  303. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  304. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  305. /// </para>
  306. /// <para>
  307. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  308. /// accessed from the same context, as required by some UI frameworks.
  309. /// </para>
  310. /// <para>
  311. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  312. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  313. /// parameter. For more information, see the remarks section on those overloads.
  314. /// </para>
  315. /// </remarks>
  316. /// <seealso cref="Observable.ToEventPattern"/>
  317. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TDelegate, TSender, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler)
  318. {
  319. if (addHandler == null)
  320. throw new ArgumentNullException(nameof(addHandler));
  321. if (removeHandler == null)
  322. throw new ArgumentNullException(nameof(removeHandler));
  323. if (scheduler == null)
  324. throw new ArgumentNullException(nameof(scheduler));
  325. return s_impl.FromEventPattern<TDelegate, TSender, TEventArgs>(addHandler, removeHandler, scheduler);
  326. }
  327. #endregion
  328. #region Action<EventHandler<TEventArgs>>
  329. /// <summary>
  330. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler{TEventArgs}"/>, to an observable sequence.
  331. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  332. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  333. /// </summary>
  334. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  335. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  336. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  337. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  338. /// <remarks>
  339. /// <para>
  340. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  341. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  342. /// </para>
  343. /// <para>
  344. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  345. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  346. /// </para>
  347. /// <para>
  348. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  349. /// making the Subscribe or Dispose call, respectively.
  350. /// </para>
  351. /// <para>
  352. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  353. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  354. /// more concise and easier to understand.
  355. /// </para>
  356. /// </remarks>
  357. /// <seealso cref="Observable.ToEventPattern"/>
  358. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(Action<EventHandler<TEventArgs>> addHandler, Action<EventHandler<TEventArgs>> removeHandler)
  359. {
  360. if (addHandler == null)
  361. throw new ArgumentNullException(nameof(addHandler));
  362. if (removeHandler == null)
  363. throw new ArgumentNullException(nameof(removeHandler));
  364. return s_impl.FromEventPattern<TEventArgs>(addHandler, removeHandler);
  365. }
  366. /// <summary>
  367. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler{TEventArgs}"/>, to an observable sequence.
  368. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  369. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  370. /// </summary>
  371. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  372. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  373. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  374. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  375. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  376. /// <remarks>
  377. /// <para>
  378. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  379. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  380. /// </para>
  381. /// <para>
  382. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  383. /// accessed from the same context, as required by some UI frameworks.
  384. /// </para>
  385. /// <para>
  386. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  387. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  388. /// parameter. For more information, see the remarks section on those overloads.
  389. /// </para>
  390. /// </remarks>
  391. /// <seealso cref="Observable.ToEventPattern"/>
  392. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(Action<EventHandler<TEventArgs>> addHandler, Action<EventHandler<TEventArgs>> removeHandler, IScheduler scheduler)
  393. {
  394. if (addHandler == null)
  395. throw new ArgumentNullException(nameof(addHandler));
  396. if (removeHandler == null)
  397. throw new ArgumentNullException(nameof(removeHandler));
  398. if (scheduler == null)
  399. throw new ArgumentNullException(nameof(scheduler));
  400. return s_impl.FromEventPattern<TEventArgs>(addHandler, removeHandler, scheduler);
  401. }
  402. #endregion
  403. #endregion
  404. #region Reflection
  405. #region Instance events
  406. /// <summary>
  407. /// Converts an instance .NET event, conforming to the standard .NET event pattern with an <see cref="EventArgs"/> parameter, to an observable sequence.
  408. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  409. /// Reflection is used to discover the event based on the target object type and the specified event name.
  410. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  411. /// </summary>
  412. /// <param name="target">Object instance that exposes the event to convert.</param>
  413. /// <param name="eventName">Name of the event to convert.</param>
  414. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  415. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> is null.</exception>
  416. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern.</exception>
  417. /// <remarks>
  418. /// <para>
  419. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  420. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  421. /// </para>
  422. /// <para>
  423. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  424. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  425. /// </para>
  426. /// <para>
  427. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  428. /// making the Subscribe or Dispose call, respectively.
  429. /// </para>
  430. /// <para>
  431. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  432. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  433. /// more concise and easier to understand.
  434. /// </para>
  435. /// </remarks>
  436. /// <seealso cref="Observable.ToEventPattern"/>
  437. public static IObservable<EventPattern<object>> FromEventPattern(object target, string eventName)
  438. {
  439. if (target == null)
  440. throw new ArgumentNullException(nameof(target));
  441. if (eventName == null)
  442. throw new ArgumentNullException(nameof(eventName));
  443. return s_impl.FromEventPattern(target, eventName);
  444. }
  445. /// <summary>
  446. /// Converts an instance .NET event, conforming to the standard .NET event pattern with an <see cref="EventArgs"/> parameter, to an observable sequence.
  447. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  448. /// Reflection is used to discover the event based on the target object type and the specified event name.
  449. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  450. /// </summary>
  451. /// <param name="target">Object instance that exposes the event to convert.</param>
  452. /// <param name="eventName">Name of the event to convert.</param>
  453. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  454. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  455. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  456. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern.</exception>
  457. /// <remarks>
  458. /// <para>
  459. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  460. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  461. /// </para>
  462. /// <para>
  463. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  464. /// accessed from the same context, as required by some UI frameworks.
  465. /// </para>
  466. /// <para>
  467. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  468. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  469. /// parameter. For more information, see the remarks section on those overloads.
  470. /// </para>
  471. /// </remarks>
  472. /// <seealso cref="Observable.ToEventPattern"/>
  473. public static IObservable<EventPattern<object>> FromEventPattern(object target, string eventName, IScheduler scheduler)
  474. {
  475. if (target == null)
  476. throw new ArgumentNullException(nameof(target));
  477. if (eventName == null)
  478. throw new ArgumentNullException(nameof(eventName));
  479. if (scheduler == null)
  480. throw new ArgumentNullException(nameof(scheduler));
  481. return s_impl.FromEventPattern(target, eventName, scheduler);
  482. }
  483. /// <summary>
  484. /// Converts an instance .NET event, conforming to the standard .NET event pattern with strongly typed event arguments, to an observable sequence.
  485. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  486. /// Reflection is used to discover the event based on the target object type and the specified event name.
  487. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  488. /// </summary>
  489. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  490. /// <param name="target">Object instance that exposes the event to convert.</param>
  491. /// <param name="eventName">Name of the event to convert.</param>
  492. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  493. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> is null.</exception>
  494. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  495. /// <remarks>
  496. /// <para>
  497. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  498. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  499. /// </para>
  500. /// <para>
  501. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  502. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  503. /// </para>
  504. /// <para>
  505. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  506. /// making the Subscribe or Dispose call, respectively.
  507. /// </para>
  508. /// <para>
  509. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  510. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  511. /// more concise and easier to understand.
  512. /// </para>
  513. /// </remarks>
  514. /// <seealso cref="Observable.ToEventPattern"/>
  515. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(object target, string eventName)
  516. {
  517. if (target == null)
  518. throw new ArgumentNullException(nameof(target));
  519. if (eventName == null)
  520. throw new ArgumentNullException(nameof(eventName));
  521. return s_impl.FromEventPattern<TEventArgs>(target, eventName);
  522. }
  523. /// <summary>
  524. /// Converts an instance .NET event, conforming to the standard .NET event pattern with strongly typed event arguments, to an observable sequence.
  525. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  526. /// Reflection is used to discover the event based on the target object type and the specified event name.
  527. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  528. /// </summary>
  529. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  530. /// <param name="target">Object instance that exposes the event to convert.</param>
  531. /// <param name="eventName">Name of the event to convert.</param>
  532. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  533. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  534. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  535. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  536. /// <remarks>
  537. /// <para>
  538. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  539. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  540. /// </para>
  541. /// <para>
  542. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  543. /// accessed from the same context, as required by some UI frameworks.
  544. /// </para>
  545. /// <para>
  546. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  547. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  548. /// parameter. For more information, see the remarks section on those overloads.
  549. /// </para>
  550. /// </remarks>
  551. /// <seealso cref="Observable.ToEventPattern"/>
  552. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(object target, string eventName, IScheduler scheduler)
  553. {
  554. if (target == null)
  555. throw new ArgumentNullException(nameof(target));
  556. if (eventName == null)
  557. throw new ArgumentNullException(nameof(eventName));
  558. if (scheduler == null)
  559. throw new ArgumentNullException(nameof(scheduler));
  560. return s_impl.FromEventPattern<TEventArgs>(target, eventName, scheduler);
  561. }
  562. /// <summary>
  563. /// Converts an instance .NET event, conforming to the standard .NET event pattern with a strongly typed sender and strongly typed event arguments, to an observable sequence.
  564. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  565. /// Reflection is used to discover the event based on the target object type and the specified event name.
  566. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  567. /// </summary>
  568. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  569. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  570. /// <param name="target">Object instance that exposes the event to convert.</param>
  571. /// <param name="eventName">Name of the event to convert.</param>
  572. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  573. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> is null.</exception>
  574. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's first argument type is not assignable to TSender. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  575. /// <remarks>
  576. /// <para>
  577. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  578. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  579. /// </para>
  580. /// <para>
  581. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  582. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  583. /// </para>
  584. /// <para>
  585. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  586. /// making the Subscribe or Dispose call, respectively.
  587. /// </para>
  588. /// <para>
  589. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  590. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  591. /// more concise and easier to understand.
  592. /// </para>
  593. /// </remarks>
  594. /// <seealso cref="Observable.ToEventPattern"/>
  595. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TSender, TEventArgs>(object target, string eventName)
  596. {
  597. if (target == null)
  598. throw new ArgumentNullException(nameof(target));
  599. if (eventName == null)
  600. throw new ArgumentNullException(nameof(eventName));
  601. return s_impl.FromEventPattern<TSender, TEventArgs>(target, eventName);
  602. }
  603. /// <summary>
  604. /// Converts an instance .NET event, conforming to the standard .NET event pattern with a strongly typed sender and strongly typed event arguments, to an observable sequence.
  605. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  606. /// Reflection is used to discover the event based on the target object type and the specified event name.
  607. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  608. /// </summary>
  609. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  610. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  611. /// <param name="target">Object instance that exposes the event to convert.</param>
  612. /// <param name="eventName">Name of the event to convert.</param>
  613. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  614. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  615. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  616. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's first argument type is not assignable to TSender. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  617. /// <remarks>
  618. /// <para>
  619. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  620. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  621. /// </para>
  622. /// <para>
  623. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  624. /// accessed from the same context, as required by some UI frameworks.
  625. /// </para>
  626. /// <para>
  627. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  628. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  629. /// parameter. For more information, see the remarks section on those overloads.
  630. /// </para>
  631. /// </remarks>
  632. /// <seealso cref="Observable.ToEventPattern"/>
  633. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TSender, TEventArgs>(object target, string eventName, IScheduler scheduler)
  634. {
  635. if (target == null)
  636. throw new ArgumentNullException(nameof(target));
  637. if (eventName == null)
  638. throw new ArgumentNullException(nameof(eventName));
  639. if (scheduler == null)
  640. throw new ArgumentNullException(nameof(scheduler));
  641. return s_impl.FromEventPattern<TSender, TEventArgs>(target, eventName, scheduler);
  642. }
  643. #endregion
  644. #region Static events
  645. /// <summary>
  646. /// Converts a static .NET event, conforming to the standard .NET event pattern with an <see cref="EventArgs"/> parameter, to an observable sequence.
  647. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  648. /// Reflection is used to discover the event based on the specified type and the specified event name.
  649. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  650. /// </summary>
  651. /// <param name="type">Type that exposes the static event to convert.</param>
  652. /// <param name="eventName">Name of the event to convert.</param>
  653. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  654. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> is null.</exception>
  655. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern.</exception>
  656. /// <remarks>
  657. /// <para>
  658. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  659. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  660. /// </para>
  661. /// <para>
  662. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  663. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  664. /// </para>
  665. /// <para>
  666. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  667. /// making the Subscribe or Dispose call, respectively.
  668. /// </para>
  669. /// <para>
  670. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  671. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  672. /// more concise and easier to understand.
  673. /// </para>
  674. /// </remarks>
  675. /// <seealso cref="Observable.ToEventPattern"/>
  676. public static IObservable<EventPattern<object>> FromEventPattern(Type type, string eventName)
  677. {
  678. if (type == null)
  679. throw new ArgumentNullException(nameof(type));
  680. if (eventName == null)
  681. throw new ArgumentNullException(nameof(eventName));
  682. return s_impl.FromEventPattern(type, eventName);
  683. }
  684. /// <summary>
  685. /// Converts a static .NET event, conforming to the standard .NET event pattern with an <see cref="EventArgs"/> parameter, to an observable sequence.
  686. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  687. /// Reflection is used to discover the event based on the specified type and the specified event name.
  688. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  689. /// </summary>
  690. /// <param name="type">Type that exposes the static event to convert.</param>
  691. /// <param name="eventName">Name of the event to convert.</param>
  692. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  693. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  694. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  695. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern.</exception>
  696. /// <remarks>
  697. /// <para>
  698. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  699. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  700. /// </para>
  701. /// <para>
  702. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  703. /// accessed from the same context, as required by some UI frameworks.
  704. /// </para>
  705. /// <para>
  706. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  707. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  708. /// parameter. For more information, see the remarks section on those overloads.
  709. /// </para>
  710. /// </remarks>
  711. /// <seealso cref="Observable.ToEventPattern"/>
  712. public static IObservable<EventPattern<object>> FromEventPattern(Type type, string eventName, IScheduler scheduler)
  713. {
  714. if (type == null)
  715. throw new ArgumentNullException(nameof(type));
  716. if (eventName == null)
  717. throw new ArgumentNullException(nameof(eventName));
  718. if (scheduler == null)
  719. throw new ArgumentNullException(nameof(scheduler));
  720. return s_impl.FromEventPattern(type, eventName, scheduler);
  721. }
  722. /// <summary>
  723. /// Converts a static .NET event, conforming to the standard .NET event pattern with strongly typed event arguments, to an observable sequence.
  724. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  725. /// Reflection is used to discover the event based on the specified type and the specified event name.
  726. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  727. /// </summary>
  728. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  729. /// <param name="type">Type that exposes the static event to convert.</param>
  730. /// <param name="eventName">Name of the event to convert.</param>
  731. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  732. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> is null.</exception>
  733. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  734. /// <remarks>
  735. /// <para>
  736. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  737. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  738. /// </para>
  739. /// <para>
  740. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  741. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  742. /// </para>
  743. /// <para>
  744. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  745. /// making the Subscribe or Dispose call, respectively.
  746. /// </para>
  747. /// <para>
  748. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  749. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  750. /// more concise and easier to understand.
  751. /// </para>
  752. /// </remarks>
  753. /// <seealso cref="Observable.ToEventPattern"/>
  754. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(Type type, string eventName)
  755. {
  756. if (type == null)
  757. throw new ArgumentNullException(nameof(type));
  758. if (eventName == null)
  759. throw new ArgumentNullException(nameof(eventName));
  760. return s_impl.FromEventPattern<TEventArgs>(type, eventName);
  761. }
  762. /// <summary>
  763. /// Converts a static .NET event, conforming to the standard .NET event pattern with strongly typed event arguments, to an observable sequence.
  764. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  765. /// Reflection is used to discover the event based on the specified type and the specified event name.
  766. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  767. /// </summary>
  768. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  769. /// <param name="type">Type that exposes the static event to convert.</param>
  770. /// <param name="eventName">Name of the event to convert.</param>
  771. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  772. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  773. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  774. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  775. /// <remarks>
  776. /// <para>
  777. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  778. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  779. /// </para>
  780. /// <para>
  781. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  782. /// accessed from the same context, as required by some UI frameworks.
  783. /// </para>
  784. /// <para>
  785. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  786. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  787. /// parameter. For more information, see the remarks section on those overloads.
  788. /// </para>
  789. /// </remarks>
  790. /// <seealso cref="Observable.ToEventPattern"/>
  791. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(Type type, string eventName, IScheduler scheduler)
  792. {
  793. if (type == null)
  794. throw new ArgumentNullException(nameof(type));
  795. if (eventName == null)
  796. throw new ArgumentNullException(nameof(eventName));
  797. if (scheduler == null)
  798. throw new ArgumentNullException(nameof(scheduler));
  799. return s_impl.FromEventPattern<TEventArgs>(type, eventName, scheduler);
  800. }
  801. /// <summary>
  802. /// Converts a static .NET event, conforming to the standard .NET event pattern with a strongly typed sender and strongly typed event arguments, to an observable sequence.
  803. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  804. /// Reflection is used to discover the event based on the specified type and the specified event name.
  805. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  806. /// </summary>
  807. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  808. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  809. /// <param name="type">Type that exposes the static event to convert.</param>
  810. /// <param name="eventName">Name of the event to convert.</param>
  811. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  812. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> is null.</exception>
  813. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's first argument type is not assignable to TSender. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  814. /// <remarks>
  815. /// <para>
  816. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  817. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  818. /// </para>
  819. /// <para>
  820. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  821. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  822. /// </para>
  823. /// <para>
  824. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  825. /// making the Subscribe or Dispose call, respectively.
  826. /// </para>
  827. /// <para>
  828. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  829. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  830. /// more concise and easier to understand.
  831. /// </para>
  832. /// </remarks>
  833. /// <seealso cref="Observable.ToEventPattern"/>
  834. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TSender, TEventArgs>(Type type, string eventName)
  835. {
  836. if (type == null)
  837. throw new ArgumentNullException(nameof(type));
  838. if (eventName == null)
  839. throw new ArgumentNullException(nameof(eventName));
  840. return s_impl.FromEventPattern<TSender, TEventArgs>(type, eventName);
  841. }
  842. /// <summary>
  843. /// Converts a static .NET event, conforming to the standard .NET event pattern with a strongly typed sender and strongly typed event arguments, to an observable sequence.
  844. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  845. /// Reflection is used to discover the event based on the specified type and the specified event name.
  846. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  847. /// </summary>
  848. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  849. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  850. /// <param name="type">Type that exposes the static event to convert.</param>
  851. /// <param name="eventName">Name of the event to convert.</param>
  852. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  853. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  854. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  855. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's first argument type is not assignable to TSender. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  856. /// <remarks>
  857. /// <para>
  858. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  859. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  860. /// </para>
  861. /// <para>
  862. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  863. /// accessed from the same context, as required by some UI frameworks.
  864. /// </para>
  865. /// <para>
  866. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  867. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  868. /// parameter. For more information, see the remarks section on those overloads.
  869. /// </para>
  870. /// </remarks>
  871. /// <seealso cref="Observable.ToEventPattern"/>
  872. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TSender, TEventArgs>(Type type, string eventName, IScheduler scheduler)
  873. {
  874. if (type == null)
  875. throw new ArgumentNullException(nameof(type));
  876. if (eventName == null)
  877. throw new ArgumentNullException(nameof(eventName));
  878. if (scheduler == null)
  879. throw new ArgumentNullException(nameof(scheduler));
  880. return s_impl.FromEventPattern<TSender, TEventArgs>(type, eventName, scheduler);
  881. }
  882. #endregion
  883. #endregion
  884. #endregion
  885. #region + FromEvent +
  886. #region Action<TDelegate>
  887. /// <summary>
  888. /// Converts a .NET event to an observable sequence, using a conversion function to obtain the event delegate. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  889. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  890. /// </summary>
  891. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  892. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  893. /// <param name="conversion">A function used to convert the given event handler to a delegate compatible with the underlying .NET event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>
  894. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  895. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  896. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  897. /// <exception cref="ArgumentNullException"><paramref name="conversion"/> or <paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  898. /// <remarks>
  899. /// <para>
  900. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  901. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  902. /// </para>
  903. /// <para>
  904. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEvent, and is used to post add and remove handler invocations.
  905. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  906. /// </para>
  907. /// <para>
  908. /// If no SynchronizationContext is present at the point of calling FromEvent, add and remove handler invocations are made synchronously on the thread
  909. /// making the Subscribe or Dispose call, respectively.
  910. /// </para>
  911. /// <para>
  912. /// It's recommended to lift FromEvent calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  913. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  914. /// more concise and easier to understand.
  915. /// </para>
  916. /// </remarks>
  917. /// <seealso cref="Observable.ToEvent"/>
  918. public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Func<Action<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
  919. {
  920. if (conversion == null)
  921. throw new ArgumentNullException(nameof(conversion));
  922. if (addHandler == null)
  923. throw new ArgumentNullException(nameof(addHandler));
  924. if (removeHandler == null)
  925. throw new ArgumentNullException(nameof(removeHandler));
  926. return s_impl.FromEvent<TDelegate, TEventArgs>(conversion, addHandler, removeHandler);
  927. }
  928. /// <summary>
  929. /// Converts a .NET event to an observable sequence, using a conversion function to obtain the event delegate. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  930. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  931. /// </summary>
  932. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  933. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  934. /// <param name="conversion">A function used to convert the given event handler to a delegate compatible with the underlying .NET event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>
  935. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  936. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  937. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  938. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  939. /// <exception cref="ArgumentNullException"><paramref name="conversion"/> or <paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  940. /// <remarks>
  941. /// <para>
  942. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  943. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  944. /// </para>
  945. /// <para>
  946. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  947. /// accessed from the same context, as required by some UI frameworks.
  948. /// </para>
  949. /// <para>
  950. /// It's recommended to lift FromEvent calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  951. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEvent that omit the IScheduler
  952. /// parameter. For more information, see the remarks section on those overloads.
  953. /// </para>
  954. /// </remarks>
  955. /// <seealso cref="Observable.ToEvent"/>
  956. public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Func<Action<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler)
  957. {
  958. if (conversion == null)
  959. throw new ArgumentNullException(nameof(conversion));
  960. if (addHandler == null)
  961. throw new ArgumentNullException(nameof(addHandler));
  962. if (removeHandler == null)
  963. throw new ArgumentNullException(nameof(removeHandler));
  964. if (scheduler == null)
  965. throw new ArgumentNullException(nameof(scheduler));
  966. return s_impl.FromEvent<TDelegate, TEventArgs>(conversion, addHandler, removeHandler, scheduler);
  967. }
  968. /// <summary>
  969. /// Converts a .NET event to an observable sequence, using a supplied event delegate type. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  970. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  971. /// </summary>
  972. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  973. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  974. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  975. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  976. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  977. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  978. /// <remarks>
  979. /// <para>
  980. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  981. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  982. /// </para>
  983. /// <para>
  984. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEvent, and is used to post add and remove handler invocations.
  985. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  986. /// </para>
  987. /// <para>
  988. /// If no SynchronizationContext is present at the point of calling FromEvent, add and remove handler invocations are made synchronously on the thread
  989. /// making the Subscribe or Dispose call, respectively.
  990. /// </para>
  991. /// <para>
  992. /// It's recommended to lift FromEvent calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  993. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  994. /// more concise and easier to understand.
  995. /// </para>
  996. /// </remarks>
  997. /// <seealso cref="Observable.ToEvent"/>
  998. public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
  999. {
  1000. if (addHandler == null)
  1001. throw new ArgumentNullException(nameof(addHandler));
  1002. if (removeHandler == null)
  1003. throw new ArgumentNullException(nameof(removeHandler));
  1004. return s_impl.FromEvent<TDelegate, TEventArgs>(addHandler, removeHandler);
  1005. }
  1006. /// <summary>
  1007. /// Converts a .NET event to an observable sequence, using a supplied event delegate type. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1008. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1009. /// </summary>
  1010. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  1011. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  1012. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1013. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1014. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  1015. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1016. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  1017. /// <remarks>
  1018. /// <para>
  1019. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1020. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1021. /// </para>
  1022. /// <para>
  1023. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  1024. /// accessed from the same context, as required by some UI frameworks.
  1025. /// </para>
  1026. /// <para>
  1027. /// It's recommended to lift FromEvent calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  1028. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEvent that omit the IScheduler
  1029. /// parameter. For more information, see the remarks section on those overloads.
  1030. /// </para>
  1031. /// </remarks>
  1032. /// <seealso cref="Observable.ToEvent"/>
  1033. public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler)
  1034. {
  1035. if (addHandler == null)
  1036. throw new ArgumentNullException(nameof(addHandler));
  1037. if (removeHandler == null)
  1038. throw new ArgumentNullException(nameof(removeHandler));
  1039. if (scheduler == null)
  1040. throw new ArgumentNullException(nameof(scheduler));
  1041. return s_impl.FromEvent<TDelegate, TEventArgs>(addHandler, removeHandler, scheduler);
  1042. }
  1043. #endregion
  1044. #region Action<Action<TEventArgs>>
  1045. /// <summary>
  1046. /// Converts a generic Action-based .NET event to an observable sequence. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1047. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1048. /// </summary>
  1049. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  1050. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1051. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1052. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1053. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  1054. /// <remarks>
  1055. /// <para>
  1056. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1057. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1058. /// </para>
  1059. /// <para>
  1060. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEvent, and is used to post add and remove handler invocations.
  1061. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  1062. /// </para>
  1063. /// <para>
  1064. /// If no SynchronizationContext is present at the point of calling FromEvent, add and remove handler invocations are made synchronously on the thread
  1065. /// making the Subscribe or Dispose call, respectively.
  1066. /// </para>
  1067. /// <para>
  1068. /// It's recommended to lift FromEvent calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  1069. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  1070. /// more concise and easier to understand.
  1071. /// </para>
  1072. /// </remarks>
  1073. /// <seealso cref="Observable.ToEvent"/>
  1074. public static IObservable<TEventArgs> FromEvent<TEventArgs>(Action<Action<TEventArgs>> addHandler, Action<Action<TEventArgs>> removeHandler)
  1075. {
  1076. if (addHandler == null)
  1077. throw new ArgumentNullException(nameof(addHandler));
  1078. if (removeHandler == null)
  1079. throw new ArgumentNullException(nameof(removeHandler));
  1080. return s_impl.FromEvent<TEventArgs>(addHandler, removeHandler);
  1081. }
  1082. /// <summary>
  1083. /// Converts a generic Action-based .NET event to an observable sequence. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1084. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1085. /// </summary>
  1086. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  1087. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1088. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1089. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  1090. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1091. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  1092. /// <remarks>
  1093. /// <para>
  1094. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1095. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1096. /// </para>
  1097. /// <para>
  1098. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  1099. /// accessed from the same context, as required by some UI frameworks.
  1100. /// </para>
  1101. /// <para>
  1102. /// It's recommended to lift FromEvent calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  1103. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEvent that omit the IScheduler
  1104. /// parameter. For more information, see the remarks section on those overloads.
  1105. /// </para>
  1106. /// </remarks>
  1107. /// <seealso cref="Observable.ToEvent"/>
  1108. public static IObservable<TEventArgs> FromEvent<TEventArgs>(Action<Action<TEventArgs>> addHandler, Action<Action<TEventArgs>> removeHandler, IScheduler scheduler)
  1109. {
  1110. if (addHandler == null)
  1111. throw new ArgumentNullException(nameof(addHandler));
  1112. if (removeHandler == null)
  1113. throw new ArgumentNullException(nameof(removeHandler));
  1114. if (scheduler == null)
  1115. throw new ArgumentNullException(nameof(scheduler));
  1116. return s_impl.FromEvent<TEventArgs>(addHandler, removeHandler, scheduler);
  1117. }
  1118. #endregion
  1119. #region Action<Action>
  1120. /// <summary>
  1121. /// Converts an Action-based .NET event to an observable sequence. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1122. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1123. /// </summary>
  1124. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1125. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1126. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1127. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  1128. /// <remarks>
  1129. /// <para>
  1130. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1131. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1132. /// </para>
  1133. /// <para>
  1134. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEvent, and is used to post add and remove handler invocations.
  1135. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  1136. /// </para>
  1137. /// <para>
  1138. /// If no SynchronizationContext is present at the point of calling FromEvent, add and remove handler invocations are made synchronously on the thread
  1139. /// making the Subscribe or Dispose call, respectively.
  1140. /// </para>
  1141. /// <para>
  1142. /// It's recommended to lift FromEvent calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  1143. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  1144. /// more concise and easier to understand.
  1145. /// </para>
  1146. /// </remarks>
  1147. /// <seealso cref="Observable.ToEvent"/>
  1148. public static IObservable<Unit> FromEvent(Action<Action> addHandler, Action<Action> removeHandler)
  1149. {
  1150. if (addHandler == null)
  1151. throw new ArgumentNullException(nameof(addHandler));
  1152. if (removeHandler == null)
  1153. throw new ArgumentNullException(nameof(removeHandler));
  1154. return s_impl.FromEvent(addHandler, removeHandler);
  1155. }
  1156. /// <summary>
  1157. /// Converts an Action-based .NET event to an observable sequence. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1158. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1159. /// </summary>
  1160. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1161. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1162. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  1163. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1164. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  1165. /// <remarks>
  1166. /// <para>
  1167. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1168. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1169. /// </para>
  1170. /// <para>
  1171. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  1172. /// accessed from the same context, as required by some UI frameworks.
  1173. /// </para>
  1174. /// <para>
  1175. /// It's recommended to lift FromEvent calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  1176. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEvent that omit the IScheduler
  1177. /// parameter. For more information, see the remarks section on those overloads.
  1178. /// </para>
  1179. /// </remarks>
  1180. /// <seealso cref="Observable.ToEvent"/>
  1181. public static IObservable<Unit> FromEvent(Action<Action> addHandler, Action<Action> removeHandler, IScheduler scheduler)
  1182. {
  1183. if (addHandler == null)
  1184. throw new ArgumentNullException(nameof(addHandler));
  1185. if (removeHandler == null)
  1186. throw new ArgumentNullException(nameof(removeHandler));
  1187. if (scheduler == null)
  1188. throw new ArgumentNullException(nameof(scheduler));
  1189. return s_impl.FromEvent(addHandler, removeHandler, scheduler);
  1190. }
  1191. #endregion
  1192. #endregion
  1193. }
  1194. }