ToDictionary.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT License.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Collections.Generic;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. namespace System.Linq
  8. {
  9. #if REFERENCE_ASSEMBLY
  10. public static partial class AsyncEnumerableDeprecated
  11. #else
  12. public static partial class AsyncEnumerable
  13. #endif
  14. {
  15. #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  16. // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.todictionaryasync?view=net-9.0-pp#system-linq-asyncenumerable-todictionaryasync-2(system-collections-generic-iasyncenumerable((-0))-system-func((-0-1))-system-collections-generic-iequalitycomparer((-1))-system-threading-cancellationtoken)
  17. // That one overload covers the next two methods, because it supplies a default comparer.
  18. /// <summary>
  19. /// Creates a dictionary from an async-enumerable sequence according to a specified key selector function.
  20. /// </summary>
  21. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  22. /// <typeparam name="TKey">The type of the dictionary key computed for each element in the source sequence.</typeparam>
  23. /// <param name="source">An async-enumerable sequence to create a dictionary for.</param>
  24. /// <param name="keySelector">A function to extract a key from each element.</param>
  25. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  26. /// <returns>An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element.</returns>
  27. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> is null.</exception>
  28. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  29. public static ValueTask<Dictionary<TKey, TSource>> ToDictionaryAsync<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, CancellationToken cancellationToken = default) where TKey : notnull =>
  30. ToDictionaryAsync(source, keySelector, comparer: null, cancellationToken);
  31. /// <summary>
  32. /// Creates a dictionary from an async-enumerable sequence according to a specified key selector function, and a comparer.
  33. /// </summary>
  34. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  35. /// <typeparam name="TKey">The type of the dictionary key computed for each element in the source sequence.</typeparam>
  36. /// <param name="source">An async-enumerable sequence to create a dictionary for.</param>
  37. /// <param name="keySelector">A function to extract a key from each element.</param>
  38. /// <param name="comparer">An equality comparer to compare keys.</param>
  39. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  40. /// <returns>An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element.</returns>
  41. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="comparer"/> is null.</exception>
  42. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  43. public static ValueTask<Dictionary<TKey, TSource>> ToDictionaryAsync<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default) where TKey : notnull
  44. {
  45. if (source == null)
  46. throw Error.ArgumentNull(nameof(source));
  47. if (keySelector == null)
  48. throw Error.ArgumentNull(nameof(keySelector));
  49. return Core(source, keySelector, comparer, cancellationToken);
  50. static async ValueTask<Dictionary<TKey, TSource>> Core(IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  51. {
  52. var d = new Dictionary<TKey, TSource>(comparer);
  53. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  54. {
  55. var key = keySelector(item);
  56. d.Add(key, item);
  57. }
  58. return d;
  59. }
  60. }
  61. #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  62. /// <summary>
  63. /// Creates a dictionary from an async-enumerable sequence by invoking a key-selector function on each element and awaiting the result.
  64. /// </summary>
  65. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  66. /// <typeparam name="TKey">The type of the dictionary key computed for each element in the source sequence.</typeparam>
  67. /// <param name="source">An async-enumerable sequence to create a dictionary for.</param>
  68. /// <param name="keySelector">An asynchronous function to extract a key from each element.</param>
  69. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  70. /// <returns>A ValueTask containing a dictionary mapping unique key values onto the corresponding source sequence's element.</returns>
  71. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> is null.</exception>
  72. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  73. [GenerateAsyncOverload]
  74. [Obsolete("Use ToDictionaryAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToDictionaryAwaitAsync functionality now exists as overloads of ToDictionaryAsync.")]
  75. private static ValueTask<Dictionary<TKey, TSource>> ToDictionaryAwaitAsyncCore<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, CancellationToken cancellationToken = default) where TKey : notnull =>
  76. ToDictionaryAwaitAsyncCore<TSource, TKey>(source, keySelector, comparer: null, cancellationToken);
  77. /// <summary>
  78. /// Creates a dictionary from an async-enumerable sequence by invoking a key-selector function on each element and awaiting the result.
  79. /// </summary>
  80. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  81. /// <typeparam name="TKey">The type of the dictionary key computed for each element in the source sequence.</typeparam>
  82. /// <param name="source">An async-enumerable sequence to create a dictionary for.</param>
  83. /// <param name="keySelector">An asynchronous function to extract a key from each element.</param>
  84. /// <param name="comparer">An equality comparer to compare keys.</param>
  85. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  86. /// <returns>A ValueTask containing a dictionary mapping unique key values onto the corresponding source sequence's element.</returns>
  87. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="comparer"/> is null.</exception>
  88. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  89. [GenerateAsyncOverload]
  90. [Obsolete("Use ToDictionaryAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToDictionaryAwaitAsync functionality now exists as overloads of ToDictionaryAsync.")]
  91. private static ValueTask<Dictionary<TKey, TSource>> ToDictionaryAwaitAsyncCore<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default) where TKey : notnull
  92. {
  93. if (source == null)
  94. throw Error.ArgumentNull(nameof(source));
  95. if (keySelector == null)
  96. throw Error.ArgumentNull(nameof(keySelector));
  97. return Core(source, keySelector, comparer, cancellationToken);
  98. static async ValueTask<Dictionary<TKey, TSource>> Core(IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  99. {
  100. var d = new Dictionary<TKey, TSource>(comparer);
  101. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  102. {
  103. var key = await keySelector(item).ConfigureAwait(false);
  104. d.Add(key, item);
  105. }
  106. return d;
  107. }
  108. }
  109. #if !NO_DEEP_CANCELLATION
  110. [GenerateAsyncOverload]
  111. [Obsolete("Use ToDictionaryAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToDictionaryAwaitWithCancellationAsync functionality now exists as overloads of ToDictionaryAsync.")]
  112. private static ValueTask<Dictionary<TKey, TSource>> ToDictionaryAwaitWithCancellationAsyncCore<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, CancellationToken cancellationToken = default) where TKey : notnull =>
  113. ToDictionaryAwaitWithCancellationAsyncCore(source, keySelector, comparer: null, cancellationToken);
  114. [GenerateAsyncOverload]
  115. [Obsolete("Use ToDictionaryAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToDictionaryAwaitWithCancellationAsync functionality now exists as overloads of ToDictionaryAsync.")]
  116. private static ValueTask<Dictionary<TKey, TSource>> ToDictionaryAwaitWithCancellationAsyncCore<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default) where TKey : notnull
  117. {
  118. if (source == null)
  119. throw Error.ArgumentNull(nameof(source));
  120. if (keySelector == null)
  121. throw Error.ArgumentNull(nameof(keySelector));
  122. return Core(source, keySelector, comparer, cancellationToken);
  123. static async ValueTask<Dictionary<TKey, TSource>> Core(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  124. {
  125. var d = new Dictionary<TKey, TSource>(comparer);
  126. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  127. {
  128. var key = await keySelector(item, cancellationToken).ConfigureAwait(false);
  129. d.Add(key, item);
  130. }
  131. return d;
  132. }
  133. }
  134. #endif
  135. #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  136. // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.todictionaryasync?view=net-9.0-pp#system-linq-asyncenumerable-todictionaryasync-2(system-collections-generic-iasyncenumerable((-0))-system-func((-0-1))-system-collections-generic-iequalitycomparer((-1))-system-threading-cancellationtoken)
  137. // The method above provides the functionality for each of the next two methods, although because it does so with a
  138. // single method that provides a default null value for the comparer, it it's not a strict source-compatible
  139. // replacement. But there's not much we can do about that.
  140. /// <summary>
  141. /// Creates a dictionary from an async-enumerable sequence according to a specified key selector function, and an element selector function.
  142. /// </summary>
  143. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  144. /// <typeparam name="TKey">The type of the dictionary key computed for each element in the source sequence.</typeparam>
  145. /// <typeparam name="TElement">The type of the dictionary value computed for each element in the source sequence.</typeparam>
  146. /// <param name="source">An async-enumerable sequence to create a dictionary for.</param>
  147. /// <param name="keySelector">A function to extract a key from each element.</param>
  148. /// <param name="elementSelector">A transform function to produce a result element value from each element.</param>
  149. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  150. /// <returns>An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element.</returns>
  151. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="elementSelector"/> is null.</exception>
  152. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  153. public static ValueTask<Dictionary<TKey, TElement>> ToDictionaryAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, CancellationToken cancellationToken = default) where TKey : notnull =>
  154. ToDictionaryAsync(source, keySelector, elementSelector, comparer: null, cancellationToken);
  155. /// <summary>
  156. /// Creates a dictionary from an async-enumerable sequence according to a specified key selector function, a comparer, and an element selector function.
  157. /// </summary>
  158. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  159. /// <typeparam name="TKey">The type of the dictionary key computed for each element in the source sequence.</typeparam>
  160. /// <typeparam name="TElement">The type of the dictionary value computed for each element in the source sequence.</typeparam>
  161. /// <param name="source">An async-enumerable sequence to create a dictionary for.</param>
  162. /// <param name="keySelector">A function to extract a key from each element.</param>
  163. /// <param name="elementSelector">A transform function to produce a result element value from each element.</param>
  164. /// <param name="comparer">An equality comparer to compare keys.</param>
  165. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  166. /// <returns>An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element.</returns>
  167. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="elementSelector"/> or <paramref name="comparer"/> is null.</exception>
  168. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  169. public static ValueTask<Dictionary<TKey, TElement>> ToDictionaryAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default) where TKey : notnull
  170. {
  171. if (source == null)
  172. throw Error.ArgumentNull(nameof(source));
  173. if (keySelector == null)
  174. throw Error.ArgumentNull(nameof(keySelector));
  175. if (elementSelector == null)
  176. throw Error.ArgumentNull(nameof(elementSelector));
  177. return Core(source, keySelector, elementSelector, comparer, cancellationToken);
  178. static async ValueTask<Dictionary<TKey, TElement>> Core(IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  179. {
  180. var d = new Dictionary<TKey, TElement>(comparer);
  181. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  182. {
  183. var key = keySelector(item);
  184. var value = elementSelector(item);
  185. d.Add(key, value);
  186. }
  187. return d;
  188. }
  189. }
  190. #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  191. /// <summary>
  192. /// Creates a dictionary from an async-enumerable sequence using the specified asynchronous key and element selector functions.
  193. /// </summary>
  194. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  195. /// <typeparam name="TKey">The type of the dictionary key computed for each element in the source sequence.</typeparam>
  196. /// <typeparam name="TElement">The type of the dictionary value computed for each element in the source sequence.</typeparam>
  197. /// <param name="source">An async-enumerable sequence to create a dictionary for.</param>
  198. /// <param name="keySelector">An asynchronous function to extract a key from each element.</param>
  199. /// <param name="elementSelector">An asynchronous transform function to produce a result element value from each element.</param>
  200. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  201. /// <returns>A ValueTask containing a dictionary mapping unique key values onto the corresponding source sequence's element.</returns>
  202. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="elementSelector"/> is null.</exception>
  203. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  204. [GenerateAsyncOverload]
  205. [Obsolete("Use ToDictionaryAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToDictionaryAwaitAsync functionality now exists as overloads of ToDictionaryAsync.")]
  206. private static ValueTask<Dictionary<TKey, TElement>> ToDictionaryAwaitAsyncCore<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, CancellationToken cancellationToken = default) where TKey : notnull =>
  207. ToDictionaryAwaitAsyncCore<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer: null, cancellationToken);
  208. /// <summary>
  209. /// Creates a dictionary from an async-enumerable sequence using the specified asynchronous key and element selector functions.
  210. /// </summary>
  211. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  212. /// <typeparam name="TKey">The type of the dictionary key computed for each element in the source sequence.</typeparam>
  213. /// <typeparam name="TElement">The type of the dictionary value computed for each element in the source sequence.</typeparam>
  214. /// <param name="source">An async-enumerable sequence to create a dictionary for.</param>
  215. /// <param name="keySelector">An asynchronous function to extract a key from each element.</param>
  216. /// <param name="elementSelector">An asynchronous transform function to produce a result element value from each element.</param>
  217. /// <param name="comparer">An equality comparer to compare keys.</param>
  218. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  219. /// <returns>A ValueTask containing a dictionary mapping unique key values onto the corresponding source sequence's element.</returns>
  220. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="elementSelector"/> or <paramref name="comparer"/> is null.</exception>
  221. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  222. [GenerateAsyncOverload]
  223. [Obsolete("Use ToDictionaryAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToDictionaryAwaitAsync functionality now exists as overloads of ToDictionaryAsync.")]
  224. private static ValueTask<Dictionary<TKey, TElement>> ToDictionaryAwaitAsyncCore<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default) where TKey : notnull
  225. {
  226. if (source == null)
  227. throw Error.ArgumentNull(nameof(source));
  228. if (keySelector == null)
  229. throw Error.ArgumentNull(nameof(keySelector));
  230. if (elementSelector == null)
  231. throw Error.ArgumentNull(nameof(elementSelector));
  232. return Core(source, keySelector, elementSelector, comparer, cancellationToken);
  233. static async ValueTask<Dictionary<TKey, TElement>> Core(IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  234. {
  235. var d = new Dictionary<TKey, TElement>(comparer);
  236. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  237. {
  238. var key = await keySelector(item).ConfigureAwait(false);
  239. var value = await elementSelector(item).ConfigureAwait(false);
  240. d.Add(key, value);
  241. }
  242. return d;
  243. }
  244. }
  245. #if !NO_DEEP_CANCELLATION
  246. [GenerateAsyncOverload]
  247. [Obsolete("Use ToDictionaryAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToDictionaryAwaitWithCancellationAsync functionality now exists as overloads of ToDictionaryAsync.")]
  248. private static ValueTask<Dictionary<TKey, TElement>> ToDictionaryAwaitWithCancellationAsyncCore<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, CancellationToken cancellationToken = default) where TKey : notnull =>
  249. ToDictionaryAwaitWithCancellationAsyncCore(source, keySelector, elementSelector, comparer: null, cancellationToken);
  250. [GenerateAsyncOverload]
  251. [Obsolete("Use ToDictionaryAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToDictionaryAwaitWithCancellationAsync functionality now exists as overloads of ToDictionaryAsync.")]
  252. private static ValueTask<Dictionary<TKey, TElement>> ToDictionaryAwaitWithCancellationAsyncCore<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default) where TKey : notnull
  253. {
  254. if (source == null)
  255. throw Error.ArgumentNull(nameof(source));
  256. if (keySelector == null)
  257. throw Error.ArgumentNull(nameof(keySelector));
  258. if (elementSelector == null)
  259. throw Error.ArgumentNull(nameof(elementSelector));
  260. return Core(source, keySelector, elementSelector, comparer, cancellationToken);
  261. static async ValueTask<Dictionary<TKey, TElement>> Core(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  262. {
  263. var d = new Dictionary<TKey, TElement>(comparer);
  264. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  265. {
  266. var key = await keySelector(item, cancellationToken).ConfigureAwait(false);
  267. var value = await elementSelector(item, cancellationToken).ConfigureAwait(false);
  268. d.Add(key, value);
  269. }
  270. return d;
  271. }
  272. }
  273. #endif
  274. }
  275. }