Observable.Events.cs 93 KB

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